<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>面试准备 - 任相鹏 完整版</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
            line-height: 1.8;
            color: #2c3e50;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            padding: 10px;
            min-height: 100vh;
            font-size: 16px;
        }
        
        @media (max-width: 768px) {
            body {
                font-size: 15px;
                padding: 5px;
            }
            
            .search-input {
                font-size: 14px;
                padding: 8px 45px 8px 35px;
            }
            
            .search-nav-btn {
                width: 32px;
                height: 32px;
                font-size: 1em;
            }
            
            .search-results-count {
                font-size: 0.75em;
                min-width: 50px;
            }
            
            .search-container {
                gap: 5px;
            }
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 16px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            overflow: visible;  /* 允许tooltip卡片显示在容器外 */
        }

        header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            text-align: center;
        }

        header h1 {
            font-size: 1.8em;
            margin-bottom: 5px;
        }

        header p {
            opacity: 0.9;
            font-size: 1em;
        }

        .search-box {
            padding: 15px;
            padding-right: 80px;  /* 为侧边栏切换按钮留空间 */
            background: #f9f9f9;
            border-bottom: 1px solid #ddd;
            position: sticky;
            top: 0;
            z-index: 100;
        }
        
        .search-container {
            position: relative;
            display: flex;
            gap: 8px;
            align-items: center;
        }
        
        .search-input-wrapper {
            flex: 1;
            position: relative;
        }

        .search-input {
            width: 100%;
            padding: 10px 50px 10px 40px;
            font-size: 15px;
            border: 2px solid #ddd;
            border-radius: 20px;
            outline: none;
            transition: all 0.3s;
        }

        .search-input:focus {
            border-color: #667eea;
            box-shadow: 0 0 8px rgba(102, 126, 234, 0.2);
        }
        
        .search-icon {
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: #667eea;
            font-size: 1.2em;
            pointer-events: none;
        }
        
        .search-clear {
            position: absolute;
            right: 12px;
            top: 50%;
            transform: translateY(-50%);
            background: #ff6b6b;
            color: white;
            border: none;
            border-radius: 50%;
            width: 22px;
            height: 22px;
            cursor: pointer;
            display: none;
            align-items: center;
            justify-content: center;
            font-size: 14px;
            line-height: 1;
            transition: all 0.3s;
        }
        
        .search-clear:hover {
            background: #ff5252;
            transform: translateY(-50%) scale(1.1);
        }
        
        .search-clear.visible {
            display: flex;
        }
        
        .search-results-count {
            min-width: 60px;
            text-align: center;
            font-size: 0.9em;
            font-weight: 600;
            padding: 8px 16px;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border-radius: 20px;
            cursor: pointer;
            transition: all 0.3s;
            box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
            display: none;
            letter-spacing: 0.5px;
        }
        
        .search-results-count.has-results {
            display: block;
        }
        
        .search-results-count:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
        }
        
        .search-results-count:active {
            transform: translateY(0);
        }
        
        .search-nav-buttons {
            display: flex;
            gap: 5px;
        }
        
        .search-nav-btn {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            border-radius: 8px;
            width: 36px;
            height: 36px;
            cursor: pointer;
            font-size: 1.1em;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s;
            opacity: 0.4;
            pointer-events: none;
        }
        
        .search-nav-btn.active {
            opacity: 1;
            pointer-events: auto;
        }
        
        .search-nav-btn.active:hover {
            transform: scale(1.1);
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
        }
        
        .search-results-count {
            font-size: 0.8em;
            color: #667eea;
            font-weight: 600;
            white-space: nowrap;
            min-width: 60px;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
            padding: 4px 8px;
            border-radius: 12px;
        }
        
        .search-results-count:hover {
            background: rgba(102, 126, 234, 0.1);
            transform: scale(1.05);
        }
        
        .search-results-count.has-results {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            animation: pulse-count 2s infinite;
        }
        
        @keyframes pulse-count {
            0%, 100% { box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.7); }
            50% { box-shadow: 0 0 0 6px rgba(102, 126, 234, 0); }
        }
        
        mark {
            background-color: #fff59d;
            padding: 2px 4px;
            border-radius: 3px;
            transition: all 0.3s;
        }
        
        mark.current {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            font-weight: 600;
            box-shadow: 0 0 10px rgba(102, 126, 234, 0.5);
            animation: pulse 0.5s ease;
        }
        
        @keyframes pulse {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.05); }
        }
        
        /* ==================== 搜索结果面板 ==================== */
        .search-results-panel {
            position: fixed;
            right: -400px;
            top: 60px;
            width: 380px;
            height: calc(100vh - 60px);
            background: white;
            box-shadow: -4px 0 20px rgba(0,0,0,0.1);
            transition: right 0.3s ease;
            z-index: 150;
            display: flex;
            flex-direction: column;
            border-left: 3px solid #667eea;
        }
        
        .search-results-panel.show {
            right: 0;
        }
        
        .search-results-header {
            padding: 15px;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            display: flex;
            flex-direction: column;
            gap: 10px;
            flex-shrink: 0;
        }
        
        .search-results-header-top {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .search-results-header h3 {
            margin: 0;
            font-size: 1.1em;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .sidebar-search-input-wrapper {
            margin: 10px 0;
        }
        
        .sidebar-search-input {
            width: 100%;
            padding: 10px 15px;
            border: 2px solid rgba(255,255,255,0.3);
            border-radius: 20px;
            font-size: 0.95em;
            background: rgba(255,255,255,0.9);
            color: #2c3e50;
            outline: none;
            transition: all 0.3s;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        
        .sidebar-search-input:focus {
            border-color: rgba(255,255,255,0.6);
            background: white;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            transform: translateY(-1px);
        }
        
        .sidebar-search-input::placeholder {
            color: #999;
        }
        
        .search-results-nav {
            display: flex;
            gap: 8px;
            align-items: center;
            padding: 12px 0 8px;
            border-top: 1px solid rgba(255,255,255,0.2);
        }
        
        .search-results-nav-btn {
            background: linear-gradient(135deg, rgba(255,255,255,0.25), rgba(255,255,255,0.15));
            color: white;
            border: none;
            border-radius: 8px;
            width: 40px;
            height: 40px;
            cursor: pointer;
            font-size: 1.3em;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
            opacity: 0.6;
            pointer-events: none;
        }
        
        .search-results-nav-btn.active {
            opacity: 1;
            pointer-events: auto;
            background: linear-gradient(135deg, rgba(255,255,255,0.35), rgba(255,255,255,0.25));
        }
        
        .search-results-nav-btn.active:hover {
            background: linear-gradient(135deg, rgba(255,255,255,0.45), rgba(255,255,255,0.35));
            transform: scale(1.1);
            box-shadow: 0 4px 12px rgba(0,0,0,0.25);
        }
        
        .search-results-nav-btn:active {
            transform: scale(0.95);
        }
        
        .search-results-counter {
            flex: 1;
            text-align: center;
            font-size: 1em;
            font-weight: 700;
            padding: 10px 16px;
            background: rgba(255,255,255,0.25);
            border-radius: 20px;
            min-width: 90px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            letter-spacing: 1px;
        }
        
        .search-results-close {
            background: rgba(255,255,255,0.2);
            border: none;
            color: white;
            width: 28px;
            height: 28px;
            border-radius: 50%;
            cursor: pointer;
            font-size: 1.2em;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s;
        }
        
        .search-results-close:hover {
            background: rgba(255,255,255,0.3);
            transform: rotate(90deg);
        }
        
        .search-results-list {
            flex: 1;
            overflow-y: auto;
            padding: 10px;
        }
        
        .search-result-item {
            padding: 12px;
            margin-bottom: 8px;
            background: #f8f9fa;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s;
            border-left: 3px solid transparent;
        }
        
        .search-result-item:hover {
            background: #e9ecef;
            border-left-color: #667eea;
            transform: translateX(3px);
        }
        
        .search-result-item.active {
            background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
            border-left-color: #764ba2;
            box-shadow: 0 2px 8px rgba(102, 126, 234, 0.2);
        }
        
        .search-result-section {
            font-size: 0.75em;
            color: #667eea;
            font-weight: 600;
            margin-bottom: 4px;
            display: flex;
            align-items: center;
            gap: 4px;
        }
        
        .search-result-context {
            font-size: 0.9em;
            line-height: 1.5;
            color: #333;
        }
        
        .search-result-context mark {
            background-color: #fff59d;
            padding: 1px 3px;
            border-radius: 2px;
            font-weight: 600;
        }
        
        .search-result-item.active .search-result-context mark {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
        }
        
        .search-results-empty {
            text-align: center;
            padding: 40px 20px;
            color: #999;
        }
        
        .search-results-empty-icon {
            font-size: 3em;
            margin-bottom: 10px;
            opacity: 0.3;
        }
        
        /* 搜索结果分组 */
        .search-results-group {
            margin-bottom: 15px;
        }
        
        .search-results-group-title {
            font-size: 0.85em;
            color: #764ba2;
            font-weight: 700;
            padding: 8px 12px;
            background: rgba(118, 75, 162, 0.05);
            border-radius: 6px;
            margin-bottom: 8px;
            display: flex;
            align-items: center;
            gap: 6px;
        }
        
        .search-toggle-panel {
            position: fixed;
            right: 10px;
            top: 120px;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            width: 45px;
            height: 45px;
            border-radius: 50%;
            cursor: pointer;
            font-size: 1.3em;
            display: none;
            align-items: center;
            justify-content: center;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
            transition: all 0.3s;
            z-index: 160;
        }
        
        .search-toggle-panel.visible {
            display: flex;
            animation: pulse-button 2s ease-in-out 3;
        }
        
        @keyframes pulse-button {
            0%, 100% {
                transform: scale(1);
                box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
            }
            50% {
                transform: scale(1.15);
                box-shadow: 0 8px 25px rgba(102, 126, 234, 0.8);
            }
        }
        
        .search-toggle-panel:hover {
            transform: scale(1.1);
            box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
        }
        
        /* 移动端适配 */
        @media (max-width: 768px) {
            .search-results-panel {
                right: -100%;
                width: 100%;
                top: 0;
            }
            
            .search-results-panel.show {
                right: 0;
            }
            
            .search-toggle-panel {
                right: 15px;
                top: 70px;
                width: 50px;
                height: 50px;
            }
        }
        
        /* ==================== 可视化组件样式 ==================== */
        .visualization-box {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            border-radius: 12px;
            padding: 20px;
            margin: 20px 0;
            box-shadow: 0 4px 15px rgba(0,0,0,0.1);
        }
        
        .visualization-box h4 {
            color: #2c3e50;
            margin-bottom: 15px;
        }
        
        .red-black-tree-demo {
            background: white;
            border-radius: 8px;
            padding: 15px;
        }
        
        .tree-controls {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
            justify-content: center;
        }
        
        .demo-btn {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            transition: all 0.3s;
            box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
        }
        
        .demo-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);
        }
        
        .demo-btn:active {
            transform: translateY(0);
        }
        
        .tree-canvas {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 15px;
            min-height: 350px;
        }
        
        .tree-canvas svg {
            display: block;
            margin: 0 auto;
        }
        
        .tree-explanation {
            text-align: center;
            margin-top: 15px;
            padding: 10px;
            background: white;
            border-radius: 6px;
            border-left: 4px solid #667eea;
        }
        
        /* 红黑树动画增强样式 */
        .tree-animation-controls {
            display: flex;
            gap: 15px;
            align-items: center;
            justify-content: center;
            margin-bottom: 15px;
            padding: 15px;
            background: white;
            border-radius: 8px;
            flex-wrap: wrap;
        }
        
        .animation-btn-group {
            display: flex;
            gap: 5px;
        }
        
        .anim-btn {
            background: #667eea;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 13px;
            font-weight: 600;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        
        .anim-btn:hover {
            background: #5568d3;
            transform: scale(1.05);
        }
        
        .anim-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
            transform: scale(1);
        }
        
        .speed-control {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .speed-label {
            font-size: 13px;
            color: #555;
            font-weight: 600;
        }
        
        .speed-btn {
            background: #f0f0f0;
            border: 2px solid #ddd;
            padding: 6px 12px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 12px;
            font-weight: 600;
            transition: all 0.2s;
        }
        
        .speed-btn.active {
            background: #667eea;
            color: white;
            border-color: #667eea;
        }
        
        .speed-btn:hover:not(.active) {
            background: #e0e0e0;
        }
        
        .step-indicator {
            padding: 8px 16px;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border-radius: 6px;
            font-size: 13px;
            font-weight: 600;
        }
        
        /* SVG动画效果 */
        .node-highlight {
            animation: pulse 0.8s ease-in-out infinite;
        }
        
        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }
        
        .node-moving {
            transition: all 1.2s cubic-bezier(0.68, -0.55, 0.265, 1.55);
        }
        
        .link-fade {
            transition: opacity 0.8s ease;
        }
        
        .link-highlight {
            stroke: #e74c3c;
            stroke-width: 4;
            animation: linkPulse 0.6s ease-in-out;
        }
        
        @keyframes linkPulse {
            0%, 100% { stroke-width: 4; }
            50% { stroke-width: 6; }
        }
        
        .node-clickable {
            cursor: pointer;
            transition: all 0.2s;
        }
        
        .node-clickable:hover {
            filter: brightness(1.2);
            transform: scale(1.1);
        }
        
        .node-info-tooltip {
            position: absolute;
            background: rgba(0, 0, 0, 0.9);
            color: white;
            padding: 10px;
            border-radius: 6px;
            font-size: 12px;
            pointer-events: none;
            z-index: 1000;
            white-space: nowrap;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
        }
        
        .tree-explanation p {
            margin: 5px 0;
            color: #2c3e50;
        }
        
        /* 节点动画 */
        @keyframes nodeHighlight {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; transform: scale(1.1); }
        }
        
        @keyframes colorChange {
            0% { fill: #e74c3c; }
            50% { fill: #f39c12; }
            100% { fill: #2c3e50; }
        }
        
        .node-animating {
            animation: nodeHighlight 1s ease-in-out;
        }
        
        .node-color-change {
            animation: colorChange 2s ease-in-out;
        }

        .nav-tabs {
            display: flex;
            background: #f5f5f5;
            border-bottom: 2px solid #ddd;
            position: sticky;
            top: 60px;
            z-index: 100;
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
        }

        .nav-tab {
            flex: 0 0 auto;
            min-width: 120px;
            padding: 12px 15px;
            text-align: center;
            cursor: pointer;
            background: #f5f5f5;
            border: none;
            font-size: 15px;
            font-weight: bold;
            transition: all 0.3s;
            color: #666;
            white-space: nowrap;
        }

        .nav-tab:hover {
            background: #e0e0e0;
        }

        .nav-tab.active {
            background: white;
            color: #667eea;
            border-bottom: 3px solid #667eea;
        }

        .content {
            padding: 20px;
            display: none;
            animation: fadeIn 0.3s;
        }

        .content.active {
            display: block;
        }

        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        h2 {
            color: #667eea;
            margin: 25px 0 15px 0;
            padding-bottom: 8px;
            border-bottom: 2px solid #667eea;
            font-size: 1.6em;
        }

        h3 {
            color: #764ba2;
            margin: 20px 0 10px 0;
            font-size: 1.3em;
        }

        h4 {
            color: #555;
            margin: 15px 0 8px 0;
            font-size: 1.1em;
        }

        p {
            margin: 12px 0;
            line-height: 1.8;
        }

        /* 谐音显示样式 */
        .term {
            position: relative;
            cursor: help;
            color: #667eea;
            font-weight: 500;
            display: inline-block;
        }

        .pronun-icon {
            font-size: 0.7em;
            margin-left: 2px;
            opacity: 0.6;
        }

        .term::after {
            content: attr(data-pronunciation);
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            background: #333;
            color: white;
            padding: 5px 10px;
            border-radius: 5px;
            font-size: 0.85em;
            white-space: nowrap;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.3s;
            margin-bottom: 5px;
            z-index: 1000;
            font-weight: normal;
        }

        .term::before {
            content: '';
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            border: 5px solid transparent;
            border-top-color: #333;
            opacity: 0;
            transition: opacity 0.3s;
            pointer-events: none;
            z-index: 1000;
        }

        /* 术语解释样式（橙色，用于专业术语） */
        .explain {
            position: relative;
            cursor: help;
            color: #ff6b35;
            font-weight: 500;
            display: inline-block;
            border-bottom: 1px dashed #ff6b35;
        }

        .explain-icon {
            font-size: 0.7em;
            margin-left: 2px;
            opacity: 0.6;
        }

        .explain::after {
            content: attr(data-explain);
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            background: #ff6b35;
            color: white;
            padding: 8px 12px;
            border-radius: 5px;
            font-size: 0.8em;
            white-space: pre-wrap;
            max-width: 300px;
            min-width: 200px;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.3s;
            margin-bottom: 5px;
            z-index: 1000;
            font-weight: normal;
            text-align: left;
            line-height: 1.5;
        }

        .explain::before {
            content: '';
            position: absolute;
            bottom: 100%;
            left: 50%;
            transform: translateX(-50%);
            border: 5px solid transparent;
            border-top-color: #ff6b35;
            opacity: 0;
            transition: opacity 0.3s;
            pointer-events: none;
            z-index: 1000;
        }

        /* 合并样式：同时有谐音和解释的术语 */
        .term.explain {
            color: #764ba2;
            border-bottom: 2px solid transparent;
            background: linear-gradient(to right, #667eea, #ff6b35);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .term.explain::after {
            content: "🔊 读音：" attr(data-pronunciation) "\A\A❓ 知识点：\A" attr(data-explain);
            white-space: pre-wrap;
            background: rgba(40, 44, 52, 0.95);
            color: #ffffff;
            padding: 15px 18px;
            border-radius: 10px;
            max-width: 450px;
            min-width: 280px;
            font-size: 0.9em;
            line-height: 1.8;
            text-align: left;
            box-shadow: 0 8px 32px rgba(0,0,0,0.4);
            border: 1px solid rgba(255,255,255,0.2);
            font-weight: normal;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
        }

        .term.explain::before {
            border-top-color: rgba(40, 44, 52, 0.95);
        }

        /* 电脑端：禁用伪元素tooltip，改用卡片 */
        @media (hover: hover) {
            .term::after,
            .term::before,
            .explain::after,
            .explain::before,
            .term.explain::after,
            .term.explain::before {
                display: none !important;
            }
        }

        /* 手机端：点击切换 */
        .term.show-pronunciation::after,
        .term.show-pronunciation::before {
            opacity: 1;
        }

        .explain.show-explain::after,
        .explain.show-explain::before {
            opacity: 1;
        }
        
        .term.explain.show-combined::after,
        .term.explain.show-combined::before {
            opacity: 1;
        }

        /* 信息框样式 */
        .tech-box {
            background: #f9f9f9;
            border-left: 4px solid #667eea;
            padding: 15px;
            margin: 12px 0;
            border-radius: 4px;
        }

        .answer-box {
            background: #fff8e1;
            border-left: 4px solid #ffc107;
            padding: 15px;
            margin: 12px 0;
            border-radius: 4px;
        }

        .example-box {
            background: #e8f5e9;
            border-left: 4px solid #4caf50;
            padding: 15px;
            margin: 12px 0;
            border-radius: 4px;
        }

        .warning-box {
            background: #ffebee;
            border-left: 4px solid #f44336;
            padding: 15px;
            margin: 12px 0;
            border-radius: 4px;
        }

        .highlight-box {
            background: #e3f2fd;
            border-left: 4px solid #2196f3;
            padding: 15px;
            margin: 12px 0;
            border-radius: 4px;
        }

        ul, ol {
            margin: 12px 0 12px 25px;
        }

        li {
            margin: 6px 0;
        }

        code {
            background: #f5f5f5;
            padding: 2px 6px;
            border-radius: 3px;
            font-family: "Courier New", monospace;
            color: #e83e8c;
            font-size: 0.9em;
        }

        pre {
            background: #2d2d2d;
            color: #f8f8f2;
            padding: 12px;
            border-radius: 5px;
            overflow-x: auto;
            margin: 12px 0;
            font-size: 0.9em;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
            font-size: 0.95em;
        }

        th, td {
            border: 1px solid #ddd;
            padding: 10px;
            text-align: left;
        }

        th {
            background: #667eea;
            color: white;
            font-weight: bold;
        }

        tr:nth-child(even) {
            background: #f9f9f9;
        }

        /* 折叠组件 */
        .collapsible {
            background: #667eea;
            color: white;
            cursor: pointer;
            padding: 12px 15px;
            border: none;
            text-align: left;
            outline: none;
            font-size: 15px;
            font-weight: bold;
            width: 100%;
            border-radius: 5px;
            margin: 8px 0;
            transition: all 0.3s;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .collapsible:hover {
            background: #764ba2;
        }

        .collapsible::after {
            content: '+';
            font-size: 20px;
            font-weight: bold;
        }

        .collapsible.active::after {
            content: '−';
        }

        .collapsible-content {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease-out;
            background: #f9f9f9;
            border-radius: 0 0 5px 5px;
        }

        .collapsible-content.active {
            padding: 15px;
            overflow: visible;  /* 允许tooltip卡片显示在容器外 */
        }

        /* 返回顶部按钮 */
        .back-to-top {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: #667eea;
            color: white;
            width: 45px;
            height: 45px;
            border-radius: 50%;
            border: none;
            font-size: 18px;
            cursor: pointer;
            display: none;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
            transition: all 0.3s;
            z-index: 1000;
        }

        .back-to-top:hover {
            background: #764ba2;
            transform: translateY(-3px);
        }
        
        /* 快速导航按钮 */
        .quick-nav-toggle {
            position: fixed;
            bottom: 75px;
            right: 20px;
            background: #764ba2;
            color: white;
            width: 45px;
            height: 45px;
            border-radius: 50%;
            border: none;
            font-size: 18px;
            cursor: pointer;
            box-shadow: 0 4px 12px rgba(118, 75, 162, 0.4);
            transition: all 0.3s;
            z-index: 1000;
        }
        
        .quick-nav-toggle:hover {
            background: #6a4291;
            transform: translateY(-3px);
        }
        
        /* 快速导航面板 */
        .quick-nav-panel {
            position: fixed;
            right: -100%;
            top: 0;
            width: 320px;
            height: 100vh;
            background: white;
            box-shadow: -4px 0 20px rgba(0, 0, 0, 0.2);
            transition: right 0.3s ease;
            z-index: 9999;
            overflow-y: auto;
            padding: 20px;
        }
        
        .quick-nav-panel.active {
            right: 0;
        }
        
        .quick-nav-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
            padding-bottom: 15px;
            border-bottom: 2px solid #667eea;
        }
        
        .quick-nav-header h3 {
            color: #667eea;
            margin: 0;
            font-size: 1.3em;
        }
        
        .quick-nav-close {
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            color: #999;
            padding: 0;
            width: 30px;
            height: 30px;
            line-height: 30px;
            text-align: center;
        }
        
        .quick-nav-close:hover {
            color: #667eea;
        }
        
        .quick-nav-section {
            margin-bottom: 20px;
        }
        
        .quick-nav-section h4 {
            color: #764ba2;
            font-size: 1em;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
        }
        
        .quick-nav-section h4::before {
            content: '▶';
            margin-right: 8px;
            font-size: 0.8em;
        }
        
        .quick-nav-items {
            list-style: none;
            padding-left: 20px;
        }
        
        .quick-nav-items li {
            margin: 8px 0;
        }
        
        .quick-nav-items a {
            color: #555;
            text-decoration: none;
            display: block;
            padding: 5px 10px;
            border-radius: 5px;
            transition: all 0.2s;
            font-size: 0.9em;
        }
        
        .quick-nav-items a:hover {
            background: #f0f0f0;
            color: #667eea;
            padding-left: 15px;
        }
        
        .quick-nav-items a.important::before {
            content: '⭐ ';
        }
        
        /* 遮罩层 */
        .nav-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.5);
            display: none;
            z-index: 9998;
        }
        
        .nav-overlay.active {
            display: block;
        }

        mark {
            background: #ffeb3b;
            padding: 2px 4px;
            border-radius: 2px;
        }

        hr {
            border: none;
            border-top: 1px solid #ddd;
            margin: 25px 0;
        }

        .star {
            color: #ffc107;
        }

        /* 移动端适配 */
        @media (max-width: 768px) {
            body {
                padding: 5px;
            }

            header h1 {
                font-size: 1.4em;
            }

            header p {
                font-size: 0.9em;
            }

            .nav-tab {
                min-width: 100px;
                padding: 10px 12px;
                font-size: 14px;
            }

            .content {
                padding: 15px;
            }

            h2 {
                font-size: 1.3em;
            }

            h3 {
                font-size: 1.15em;
            }

            h4 {
                font-size: 1em;
            }

            .back-to-top {
                width: 40px;
                height: 40px;
                bottom: 15px;
                right: 15px;
                font-size: 16px;
            }
            
            .quick-nav-toggle {
                width: 40px;
                height: 40px;
                bottom: 65px;
                right: 15px;
                font-size: 16px;
            }
            
            .quick-nav-panel {
                width: 90vw;
                right: -90vw;
                padding: 15px;
            }
            
            .quick-nav-header h3 {
                font-size: 1.1em;
            }
            
            .quick-nav-items a {
                font-size: 0.85em;
            }

            table {
                font-size: 0.85em;
            }

            th, td {
                padding: 8px;
            }

            .search-box {
                top: 0;
            }

            .nav-tabs {
                top: 55px;
            }
        }

        /* ==================== 移动端炫酷UI ==================== */
        
        /* 移动端底部抽屉卡片 */
        .mobile-tooltip-card {
            display: none;
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            background: rgba(255, 255, 255, 0.98);
            backdrop-filter: blur(25px) saturate(180%);
            -webkit-backdrop-filter: blur(25px) saturate(180%);
            border-radius: 25px 25px 0 0;
            box-shadow: 0 -8px 40px rgba(0,0,0,0.15), 0 -2px 10px rgba(0,0,0,0.05);
            transform: translateY(100%);
            transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
            z-index: 10000;
            max-height: 80vh;
            overflow-y: auto;
            opacity: 0;
            border: 2px solid rgba(255,255,255,0.5);
        }
        
        .mobile-tooltip-card.show {
            display: block;
            transform: translateY(0);
            opacity: 1;
        }
        
        /* 电脑端：居中显示卡片 */
        @media (hover: hover) and (min-width: 769px) {
            .mobile-tooltip-card {
                bottom: auto;
                left: 50%;
                right: auto;
                top: 50%;
                transform: translate(-50%, -50%) scale(0.9);
                border-radius: 20px;
                max-width: 550px;
                max-height: 70vh;
            }
            
            .mobile-tooltip-card.show {
                transform: translate(-50%, -50%) scale(1);
            }
            
            .mobile-tooltip-handle {
                display: none;
            }
        }
        
        /* 拖动指示器 */
        .mobile-tooltip-handle {
            width: 40px;
            height: 5px;
            background: linear-gradient(to right, #667eea, #764ba2);
            border-radius: 3px;
            margin: 12px auto 15px;
            opacity: 0.5;
            box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
        }
        
        /* 卡片内容区 */
        .mobile-tooltip-content {
            padding: 0 20px 25px;
        }
        
        /* 术语标题 */
        .mobile-term-title {
            font-size: 1.4em;
            font-weight: 700;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #ff6b35 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            margin-bottom: 20px;
            text-align: center;
            letter-spacing: 1px;
        }
        
        /* 谐音区 */
        .mobile-pronunciation-section {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 18px 20px;
            border-radius: 15px;
            margin-bottom: 15px;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
            position: relative;
            overflow: hidden;
        }
        
        .mobile-pronunciation-section::before {
            content: '';
            position: absolute;
            top: -50%;
            right: -50%;
            width: 200%;
            height: 200%;
            background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
            animation: shine 3s infinite;
        }
        
        @keyframes shine {
            0%, 100% { transform: rotate(0deg); }
            50% { transform: rotate(180deg); }
        }
        
        .mobile-pronunciation-label {
            font-size: 0.85em;
            opacity: 0.9;
            margin-bottom: 8px;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        
        .mobile-pronunciation-value {
            font-size: 1.5em;
            font-weight: 700;
            letter-spacing: 2px;
            position: relative;
            z-index: 1;
        }
        
        /* 解释区 */
        .mobile-explain-section {
            background: linear-gradient(135deg, #fff8f0 0%, #ffe8e0 100%);
            padding: 18px 20px;
            border-radius: 15px;
            border-left: 5px solid #ff6b35;
            box-shadow: 0 4px 15px rgba(255, 107, 53, 0.15);
            position: relative;
        }
        
        .mobile-explain-label {
            font-size: 0.9em;
            color: #ff6b35;
            font-weight: 700;
            margin-bottom: 12px;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        
        .mobile-explain-value {
            color: #333;
            line-height: 1.8;
            font-size: 0.95em;
            white-space: pre-wrap;
        }
        
        /* 关闭按钮 */
        .mobile-close-btn {
            position: absolute;
            top: 15px;
            right: 15px;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            background: rgba(0,0,0,0.05);
            border: none;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: all 0.3s;
            z-index: 10;
        }
        
        .mobile-close-btn:active {
            transform: scale(0.9);
            background: rgba(0,0,0,0.1);
        }
        
        /* 遮罩层 */
        .mobile-overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.4);
            backdrop-filter: blur(3px);
            z-index: 9999;
            opacity: 0;
            transition: opacity 0.3s ease;
        }
        
        .mobile-overlay.show {
            display: block;
            opacity: 1;
        }
        
        /* 波纹效果 */
        .ripple {
            position: absolute;
            border-radius: 50%;
            background: rgba(102, 126, 234, 0.5);
            transform: scale(0);
            animation: ripple-animation 0.6s ease-out;
            pointer-events: none;
        }
        
        @keyframes ripple-animation {
            to {
                transform: scale(4);
                opacity: 0;
            }
        }
        
        /* 移动端优化 */
        @media (max-width: 768px) {
            /* 禁用桌面端tooltip */
            .term:not(.term.explain):hover::after,
            .term:not(.term.explain):hover::before,
            .explain:not(.term.explain):hover::after,
            .explain:not(.term.explain):hover::before,
            .term.explain:hover::after,
            .term.explain:hover::before {
                opacity: 0 !important;
            }
            
            /* 术语添加点击视觉反馈 */
            .term, .explain {
                position: relative;
                overflow: hidden;
            }
        }

        /* 打印样式 */
        @media print {
            body {
                background: white;
                padding: 0;
            }

            .container {
                box-shadow: none;
            }

            .search-box,
            .nav-tabs,
            .back-to-top,
            .collapsible {
                display: none;
            }

            .content {
                display: block !important;
                padding: 20px;
            }

            .collapsible-content {
                max-height: none !important;
                display: block !important;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>🎯 面试准备 - 完整版</h1>
            <p>任相鹏 | 大数据 & AI 开发 | 8年经验 | 电话：17610243355</p>
        </header>

        <div class="search-box">
            <div class="search-container">
                <div class="search-input-wrapper">
                    <span class="search-icon">🔍</span>
                    <input type="text" class="search-input" id="searchInput" placeholder="搜索技术术语、项目名称...（支持Enter键）">
                    <button class="search-clear" id="searchClear" title="清空搜索">✕</button>
                </div>
            </div>
        </div>

        <div class="nav-tabs">
            <button class="nav-tab active" onclick="switchTab(0)">📋 个人简历</button>
            <button class="nav-tab" onclick="switchTab(1)">🤖 AI技术详解</button>
            <button class="nav-tab" onclick="switchTab(2)">💾 大数据技术</button>
            <button class="nav-tab" onclick="switchTab(3)">☕ Java后端</button>
            <button class="nav-tab" onclick="switchTab(4)">🎨 前端基础</button>
            <button class="nav-tab" onclick="switchTab(5)">🐍 Python基础</button>
            <button class="nav-tab" onclick="switchTab(6)">💼 项目介绍</button>
            <button class="nav-tab" onclick="switchTab(7)">🎯 应对策略</button>
        </div>

        <!-- 内容区域将在后续步骤中添加 -->
        <div class="content active" id="content0">
            <h2>📋 个人简历</h2>
            
            <h3>基本信息</h3>
            <table>
                <tr>
                    <th>项目</th>
                    <th>内容</th>
                </tr>
                <tr>
                    <td><strong>姓名</strong></td>
                    <td>任相鹏</td>
                </tr>
                <tr>
                    <td><strong>性别</strong></td>
                    <td>男</td>
                </tr>
                <tr>
                    <td><strong>年龄</strong></td>
                    <td>28岁</td>
                </tr>
                <tr>
                    <td><strong>电话</strong></td>
                    <td>17610243355</td>
                </tr>
                <tr>
                    <td><strong>邮箱</strong></td>
                    <td>635887935@qq.com</td>
                </tr>
                <tr>
                    <td><strong>工作经验</strong></td>
                    <td>8年</td>
                </tr>
                <tr>
                    <td><strong>求职意向</strong></td>
                    <td>数据开发</td>
                </tr>
                <tr>
                    <td><strong>期望薪资</strong></td>
                    <td>面议</td>
                </tr>
                <tr>
                    <td><strong>期望城市</strong></td>
                    <td>北京</td>
                </tr>
            </table>

            <hr>

            <h3>个人优势</h3>
            
            <div class="highlight-box">
                <h4>1. <span class="term" data-pronunciation="诶爱">AI<span class="pronun-icon">🔊</span></span> 智能体开发能力</h4>
                <p>熟练掌握 <span class="term" data-pronunciation="诶兔诶">A2A<span class="pronun-icon">🔊</span></span> 协议实现多智能体协作，开源大模型本地化部署与 <span class="term" data-pronunciation="撇破母特">Prompt<span class="pronun-icon">🔊</span></span> 工程优化，集成 <span class="term" data-pronunciation="Q问">Qwen<span class="pronun-icon">🔊</span></span>2/Qwen2.5/Qwen3、<span class="term" data-pronunciation="地破席克">DeepSeek<span class="pronun-icon">🔊</span></span>-V2/V3、<span class="term" data-pronunciation="洽特-G-L-M">ChatGLM<span class="pronun-icon">🔊</span></span>3/4 等开源模型，具备向量检索（<span class="term" data-pronunciation="拉格">RAG<span class="pronun-icon">🔊</span></span>）、语音识别、多模态处理的端到端开发与私有化部署经验，紧跟大模型技术演进，有多次模型版本升级与迁移经验。</p>
            </div>

            <div class="highlight-box">
                <h4>2. <span class="term" data-pronunciation="家哇">Java<span class="pronun-icon">🔊</span></span> 业务与工程能力</h4>
                <p><span class="term" data-pronunciation="斯不ring 布特">Spring Boot<span class="pronun-icon">🔊</span></span> 生态与 <span class="term" data-pronunciation="都宝">Dubbo<span class="pronun-icon">🔊</span></span> 分布式框架熟练，<span class="term" data-pronunciation="迈百踢斯-夫来克斯">MyBatis-Flex<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="麦普-斯茶克特">MapStruct<span class="pronun-icon">🔊</span></span> 工程化实践，具备开源组件与平台后端的二开与性能优化能力。</p>
            </div>

            <div class="highlight-box">
                <h4>3. 大数据开发与优化能力</h4>
                <p><span class="term explain" data-pronunciation="夫林克" data-explain="Apache Flink，实时流式计算引擎。

✦ 核心能力：
• 流批一体：同一套代码处理流数据和批数据
• 低延迟：毫秒级延迟，比Spark Streaming快
• 高吞吐：单机百万QPS
• Exactly-Once：数据不重不丢

✦ 核心概念：
• Checkpoint：状态快照，故障恢复
• Watermark：处理乱序数据的时间戳机制
• 状态管理：TB级状态数据（RocksDB）
• 反压：下游慢了自动降速

✦ vs Spark：
• Flink真流处理，Spark是微批
• Flink延迟更低（毫秒 vs 秒）
• Flink状态管理更强
• Spark生态更成熟

✦ 项目中的应用：
• 实时交易计算（5000万+笔/天）
• 实时数据入湖（Flink+Hudi）
• CDC数据同步（Oracle→数据湖）
• 实时风控（状态累计、规则触发）">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> on <span class="term" data-pronunciation="库伯耐踢斯">K8s<span class="pronun-icon">🔊</span></span> 与 <span class="term" data-pronunciation="压恩">Yarn<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="斯爸克">Spark<span class="pronun-icon">🔊</span></span> 作业开发，<span class="term" data-pronunciation="夫林克思Q偶">FlinkSQL<span class="pronun-icon">🔊</span></span>、<span class="term explain" data-pronunciation="切克破因特" data-explain="Flink的状态快照机制，类似游戏存档。

✦ 为什么需要：
• 故障恢复：任务挂了从Checkpoint恢复
• 数据不丢不重：Exactly-Once语义
• 状态管理：TB级状态数据持久化

✦ 工作原理：
• JobManager触发Checkpoint
• 向Source插入Barrier标记
• Barrier随数据流向下游传递
• 每个算子保存状态快照
• 上传到HDFS/S3等存储
• 全部完成后标记为成功

✦ 配置要点：
• 间隔时间：5-10分钟（太频繁影响性能）
• 超时时间：10分钟（避免长时间阻塞）
• 最大并发：1-2个（避免资源竞争）
• 状态后端：RocksDB（大状态场景）

✦ vs Savepoint：
• Checkpoint自动触发，Savepoint手动触发
• Checkpoint定期删除，Savepoint需手动删除
• Savepoint用于升级/迁移">Checkpoint<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term" data-pronunciation="赛无破因特">Savepoint<span class="pronun-icon">🔊</span></span> 管理，<span class="term" data-pronunciation="H-D-F-S">HDFS<span class="pronun-icon">🔊</span></span> 与 <span class="term" data-pronunciation="卡夫卡">Kafka<span class="pronun-icon">🔊</span></span> 性能与稳定性调优。</p>
            </div>

            <div class="highlight-box">
                <h4>4. 集群与运维能力</h4>
                <p>K8s 与裸机部署的 <span class="term" data-pronunciation="哈嘟破">Hadoop<span class="pronun-icon">🔊</span></span> 生态独立交付，x86、<span class="term" data-pronunciation="阿姆">ARM<span class="pronun-icon">🔊</span></span>、飞腾兼容，掌握 <span class="term" data-pronunciation="H-A，高可用">HA<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="可波肉斯">Kerberos<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="N-F-S">NFS<span class="pronun-icon">🔊</span></span>+<span class="term" data-pronunciation="P-V-C">PVC<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="印格瑞斯">Ingress<span class="pronun-icon">🔊</span></span>，快速排障与文档化。</p>
            </div>

            <div class="highlight-box">
                <h4>5. 组件集成与适配能力</h4>
                <p><span class="term" data-pronunciation="key巴纳">Kibana<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="Q由比">Kyuubi<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="特兹">Tez<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="杰普林">Zeppelin<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="菲尼克斯">Phoenix<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="多瑞斯">Doris<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="斯达若克斯">StarRocks<span class="pronun-icon">🔊</span></span>、Kafka Eagle、<span class="term" data-pronunciation="劳格-斯踏虚">Logstash<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="润jer">Ranger<span class="pronun-icon">🔊</span></span>(<span class="term" data-pronunciation="K-M-S">KMS<span class="pronun-icon">🔊</span></span>)、<span class="term" data-pronunciation="juice-F-S">JuiceFS<span class="pronun-icon">🔊</span></span> 等接入与一键化管理，<span class="term" data-pronunciation="海无">Hive<span class="pronun-icon">🔊</span></span> 与 Ranger 的达梦适配、<span class="term" data-pronunciation="克里克豪斯">ClickHouse<span class="pronun-icon">🔊</span></span> 的飞腾适配源码级改造。</p>
            </div>

            <div class="highlight-box">
                <h4>6. 平台工程化能力</h4>
                <p><span class="term" data-pronunciation="詹金斯">Jenkins<span class="pronun-icon">🔊</span></span> <span class="term" data-pronunciation="C-I斜杠C-D">CI/CD<span class="pronun-icon">🔊</span></span> 与 <span class="term explain" data-pronunciation="多克" data-explain="容器技术，轻量级虚拟化方案。

✦ 核心概念：
• 镜像(Image)：只读模板，包含代码+依赖
• 容器(Container)：镜像的运行实例
• 仓库(Registry)：存储镜像，如DockerHub

✦ vs 虚拟机：
• Docker更轻量：共享宿主机内核
• 启动快：秒级 vs 分钟级
• 资源占用少：MB级 vs GB级
• 隔离性较弱：共享内核

✦ 核心优势：
• 环境一致：开发、测试、生产环境完全一致
• 快速部署：秒级启动
• 易迁移：一次构建，到处运行
• 资源隔离：进程、网络、文件系统隔离

✦ 项目中的应用：
• 智能体任务隔离：每个任务独立容器
• 微服务部署：每个服务一个容器
• CI/CD：构建、测试、部署自动化

✦ 面试要点：
• Dockerfile：定义镜像构建步骤
• 数据卷：持久化数据
• 网络模式：bridge/host/none">Docker<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 镜像规范化，统一接口与版本控制，<span class="term" data-pronunciation="S-S-E">SSE<span class="pronun-icon">🔊</span></span> 与 <span class="term" data-pronunciation="外柏搜ket">WebSocket<span class="pronun-icon">🔊</span></span> 实时日志链路，<span class="term" data-pronunciation="破米修斯">Prometheus<span class="pronun-icon">🔊</span></span>+<span class="term" data-pronunciation="格拉发那">Grafana<span class="pronun-icon">🔊</span></span> 可观测性建设。</p>
            </div>

            <div class="highlight-box">
                <h4>7. 前端技术能力（AI 加持）</h4>
                <p>可独立完成 <span class="term" data-pronunciation="V由">Vue<span class="pronun-icon">🔊</span></span>2/Vue3、<span class="term" data-pronunciation="瑞爱克特">React<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="耐克斯特点J-S">Next.js<span class="pronun-icon">🔊</span></span> 开发，掌握路由、状态管理、SSE 与 WebSocket、组件封装、<span class="term" data-pronunciation="泰普斯克瑞特">TypeScript<span class="pronun-icon">🔊</span></span>、工程化发布（<span class="term" data-pronunciation="V特">Vite<span class="pronun-icon">🔊</span></span>/<span class="term" data-pronunciation="外柏派克">Webpack<span class="pronun-icon">🔊</span></span>）、UI 体系（<span class="term" data-pronunciation="太偶win的">Tailwind<span class="pronun-icon">🔊</span></span>、Ant Design、Element、shadcn/ui）。</p>
            </div>

            <hr>

            <h3>工作经历</h3>

            <button class="collapsible">北京中兵数字科技集团有限公司 | AI开发工程师/全栈工程师 | 2024.09-2025.11</button>
            <div class="collapsible-content">
                <ol>
                    <li>负责军工企业内网环境下的智能问答与数据分析平台架构设计与核心开发，基于 <span class="term" data-pronunciation="拉格">RAG<span class="pronun-icon">🔊</span></span>（检索增强生成）技术实现文档智能问答、<span class="term" data-pronunciation="特克斯特兔思Q偶">Text2SQL<span class="pronun-icon">🔊</span></span> 自然语言查询、多模态数据分析等核心功能，采用开源大模型本地化部署方案，满足数据安全和内网隔离要求，支持多租户、权限管控、审计日志等企业级特性。</li>
                    
                    <li>负责大模型私有化部署与推理优化，本地部署 Qwen2.5/Qwen3、DeepSeek-V2/V3 等开源大模型（2024.09 初期采用 Qwen2.5-14B，2025.05 升级至 Qwen3-14B），使用 <span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span> 推理框架和 <span class="term" data-pronunciation="夫拉虚腾神2">FlashAttention2<span class="pronun-icon">🔊</span></span> 加速技术，配合模型量化（<span class="term" data-pronunciation="印特8/印特4">INT8/INT4<span class="pronun-icon">🔊</span></span>）和批处理优化，显著提升推理吞吐量，首字延迟控制在 500ms 以内，满足生产环境实时交互需求。</li>
                    
                    <li>负责向量检索引擎的设计与优化，本地部署 <span class="term" data-pronunciation="B-G-E-M3">BGE-M3<span class="pronun-icon">🔊</span></span> 多语言向量模型和 <span class="term" data-pronunciation="鸡踢伊-Q问2-7B">gte-Qwen2-7B<span class="pronun-icon">🔊</span></span> 重排序模型，基于 <span class="term" data-pronunciation="米偶乌斯">Milvus<span class="pronun-icon">🔊</span></span> 向量数据库实现毫秒级语义检索，通过混合检索（向量+<span class="term" data-pronunciation="B-M-25">BM25<span class="pronun-icon">🔊</span></span>）、二阶段重排序、Query 改写等技术显著提升召回效果，支持百万级文档的实时检索。</li>
                    
                    <li>设计并实现 <span class="term" data-pronunciation="特克斯特兔思Q偶">Text2SQL<span class="pronun-icon">🔊</span></span> 智能查询引擎，使用 <span class="term" data-pronunciation="Q问2.5-扣德-7B">Qwen2.5-Coder-7B<span class="pronun-icon">🔊</span></span> 模型本地部署，通过 <span class="term" data-pronunciation="斯key马">Schema<span class="pronun-icon">🔊</span></span> 向量化 + <span class="term" data-pronunciation="few秀特">Few-shot<span class="pronun-icon">🔊</span></span> 示例 + <span class="term" data-pronunciation="chen-of-扫特">Chain-of-Thought<span class="pronun-icon">🔊</span></span> <span class="term" data-pronunciation="撇破母特">Prompt<span class="pronun-icon">🔊</span></span> 工程实现自然语言到 <span class="term" data-pronunciation="思Q偶">SQL<span class="pronun-icon">🔊</span></span> 的自动转换，支持 <span class="term explain" data-pronunciation="迈思Q偶" data-explain="最流行的开源关系型数据库。

✦ 核心特点：
• 开源免费（社区版）
• 高性能：支持千万级数据
• ACID事务：保证数据一致性
• 主从复制：读写分离、高可用

✦ 存储引擎：
• InnoDB：支持事务，行级锁，默认引擎
• MyISAM：不支持事务，表级锁，已过时

✦ 优化要点：
• 索引：B+树，加速查询
• 慢查询优化：EXPLAIN分析
• 分库分表：应对大数据量
• 读写分离：主写从读

✦ 面试高频问题：
• 索引原理？→ B+树，叶子节点存数据
• 事务隔离级别？→ 读未提交/读已提交/可重复读/串行化
• 如何避免慢查询？→ 加索引、避免全表扫描
• 主从延迟怎么办？→ 并行复制、半同步复制">MySQL<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term" data-pronunciation="泼斯特格瑞思Q偶">PostgreSQL<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="欧瑞口">Oracle<span class="pronun-icon">🔊</span></span>、ClickHouse 等多种数据库，集成语法校验和安全沙箱机制（禁止危险操作、自动添加 LIMIT），满足企业生产环境使用需求。</li>
                    
                    <li>负责 AI 智能体平台 <span class="term" data-pronunciation="苏破诶真特">SuperAgent<span class="pronun-icon">🔊</span></span> 的架构设计与开发，基于 <span class="term" data-pronunciation="姑狗">Google<span class="pronun-icon">🔊</span></span> A2A 协议实现多智能体协作框架，集成 Qwen2.5/Qwen3、DeepSeek-V2/V3、ChatGLM3/4 等开源大模型，支持 PPT 生成、文档处理、代码执行等复杂任务的智能化处理，通过容器化环境（Docker）实现任务执行过程的隔离与可视化。</li>
                    
                    <li>负责系统性能优化与工程化建设，实现相似问题缓存（余弦相似度 > 0.95 直接返回）、异步任务队列（<span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>）、流式输出（<span class="term" data-pronunciation="外柏搜ket">WebSocket<span class="pronun-icon">🔊</span></span>/<span class="term" data-pronunciation="S-S-E">SSE<span class="pronun-icon">🔊</span></span>）、限流降级（<span class="term" data-pronunciation="森踢纳偶">Sentinel<span class="pronun-icon">🔊</span></span>）等高并发优化方案，构建基于 <span class="term" data-pronunciation="踹疼">Triton<span class="pronun-icon">🔊</span></span> Inference Server 的统一推理平台（集成 vLLM 引擎处理 LLM、<span class="term" data-pronunciation="腾色-阿-踢">TensorRT<span class="pronun-icon">🔊</span></span> 处理向量模型），实现模型负载均衡与动态扩缩容，有效降低推理成本，系统响应延迟控制在 3 秒以内。</li>
                </ol>
            </div>

            <button class="collapsible">北京科杰科技有限公司 | 大数据开发工程师 | 2021.12-2024.08</button>
            <div class="collapsible-content">
                <ol>
                    <li>负责某中央银行核心业务系统的实时数据处理平台开发，承载全行交易系统、风控系统、监管报送等业务，日均处理交易流水 <strong>5000 万+ 笔</strong>、数据量 <strong>2TB+</strong>，通过 Flink 实时计算 + <span class="term explain" data-pronunciation="呼嘀" data-explain="Apache Hudi，流式数据湖平台。

✦ 核心功能：
• 流批一体：实时写入+批量查询
• ACID事务：保证数据一致性
• 增量消费：只读变更数据
• 时间旅行：查询历史版本数据
• Upsert/Delete：支持更新和删除

✦ 两种表类型：
• COW（Copy On Write）：写时复制，查询快
• MOR（Merge On Read）：读时合并，写入快

✦ 核心概念：
• Timeline：记录所有操作历史
• Compaction：合并小文件，优化查询
• Cleaner：清理过期数据
• Index：加速Upsert（Bloom Filter/HBase）

✦ vs Iceberg/Delta Lake：
• Hudi流式写入性能更好
• Iceberg元数据管理更强
• Delta专注Spark生态

✦ 项目中的应用：
• 实时数据入湖：Flink→Hudi→HDFS
• CDC同步：捕获数据库变更
• 数据更新：支持Upsert，不用全量替换">Hudi<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 数据湖架构实现流批一体化数据管理，为监管报送、实时风控、反洗钱监测等业务提供秒级数据支撑。</li>
                    
                    <li>负责 <span class="term explain" data-pronunciation="夫林克" data-explain="Apache Flink，实时流式计算引擎。

✦ 核心能力：
• 流批一体：同一套代码处理流数据和批数据
• 低延迟：毫秒级延迟，比Spark Streaming快
• 高吞吐：单机百万QPS
• Exactly-Once：数据不重不丢

✦ 核心概念：
• Checkpoint：状态快照，故障恢复
• Watermark：处理乱序数据的时间戳机制
• 状态管理：TB级状态数据（RocksDB）
• 反压：下游慢了自动降速

✦ vs Spark：
• Flink真流处理，Spark是微批
• Flink延迟更低（毫秒 vs 秒）
• Flink状态管理更强
• Spark生态更成熟

✦ 项目中的应用：
• 实时交易计算（5000万+笔/天）
• 实时数据入湖（Flink+Hudi）
• CDC数据同步（Oracle→数据湖）
• 实时风控（状态累计、规则触发）">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 作业性能优化，解决 <span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 消费延迟和反压问题，通过 <span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 分区优化（扩展至 256 分区）、算子链优化、异步 <span class="term" data-pronunciation="I-O">IO<span class="pronun-icon">🔊</span></span>、<span class="term explain" data-pronunciation="切克破因特" data-explain="Flink的状态快照机制，类似游戏存档。

✦ 为什么需要：
• 故障恢复：任务挂了从Checkpoint恢复
• 数据不丢不重：Exactly-Once语义
• 状态管理：TB级状态数据持久化

✦ 工作原理：
• JobManager触发Checkpoint
• 向Source插入Barrier标记
• Barrier随数据流向下游传递
• 每个算子保存状态快照
• 上传到HDFS/S3等存储
• 全部完成后标记为成功

✦ 配置要点：
• 间隔时间：5-10分钟（太频繁影响性能）
• 超时时间：10分钟（避免长时间阻塞）
• 最大并发：1-2个（避免资源竞争）
• 状态后端：RocksDB（大状态场景）

✦ vs Savepoint：
• Checkpoint自动触发，Savepoint手动触发
• Checkpoint定期删除，Savepoint需手动删除
• Savepoint用于升级/迁移">Checkpoint<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 调优等手段，显著提升吞吐量并降低处理延迟，<span class="term explain" data-pronunciation="切克破因特" data-explain="Flink的状态快照机制，类似游戏存档。

✦ 为什么需要：
• 故障恢复：任务挂了从Checkpoint恢复
• 数据不丢不重：Exactly-Once语义
• 状态管理：TB级状态数据持久化

✦ 工作原理：
• JobManager触发Checkpoint
• 向Source插入Barrier标记
• Barrier随数据流向下游传递
• 每个算子保存状态快照
• 上传到HDFS/S3等存储
• 全部完成后标记为成功

✦ 配置要点：
• 间隔时间：5-10分钟（太频繁影响性能）
• 超时时间：10分钟（避免长时间阻塞）
• 最大并发：1-2个（避免资源竞争）
• 状态后端：RocksDB（大状态场景）

✦ vs Savepoint：
• Checkpoint自动触发，Savepoint手动触发
• Checkpoint定期删除，Savepoint需手动删除
• Savepoint用于升级/迁移">Checkpoint<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 稳定性大幅提升。</li>
                    
                    <li>负责基于 <span class="term explain" data-pronunciation="呼嘀" data-explain="Apache Hudi，流式数据湖平台。

✦ 核心功能：
• 流批一体：实时写入+批量查询
• ACID事务：保证数据一致性
• 增量消费：只读变更数据
• 时间旅行：查询历史版本数据
• Upsert/Delete：支持更新和删除

✦ 两种表类型：
• COW（Copy On Write）：写时复制，查询快
• MOR（Merge On Read）：读时合并，写入快

✦ 核心概念：
• Timeline：记录所有操作历史
• Compaction：合并小文件，优化查询
• Cleaner：清理过期数据
• Index：加速Upsert（Bloom Filter/HBase）

✦ vs Iceberg/Delta Lake：
• Hudi流式写入性能更好
• Iceberg元数据管理更强
• Delta专注Spark生态

✦ 项目中的应用：
• 实时数据入湖：Flink→Hudi→HDFS
• CDC同步：捕获数据库变更
• 数据更新：支持Upsert，不用全量替换">Hudi<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 的实时数据湖架构设计与开发，实现 <span class="term explain" data-pronunciation="夫林克" data-explain="Apache Flink，实时流式计算引擎。

✦ 核心能力：
• 流批一体：同一套代码处理流数据和批数据
• 低延迟：毫秒级延迟，比Spark Streaming快
• 高吞吐：单机百万QPS
• Exactly-Once：数据不重不丢

✦ 核心概念：
• Checkpoint：状态快照，故障恢复
• Watermark：处理乱序数据的时间戳机制
• 状态管理：TB级状态数据（RocksDB）
• 反压：下游慢了自动降速

✦ vs Spark：
• Flink真流处理，Spark是微批
• Flink延迟更低（毫秒 vs 秒）
• Flink状态管理更强
• Spark生态更成熟

✦ 项目中的应用：
• 实时交易计算（5000万+笔/天）
• 实时数据入湖（Flink+Hudi）
• CDC数据同步（Oracle→数据湖）
• 实时风控（状态累计、规则触发）">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 消费 <span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 数据实时入湖到 <span class="term" data-pronunciation="H-D-F-S">HDFS<span class="pronun-icon">🔊</span></span>，支持 <span class="term" data-pronunciation="C-D-C">CDC<span class="pronun-icon">🔊</span></span> 捕获上游数据库变更，通过 <span class="term" data-pronunciation="靠/摸">COW/MOR<span class="pronun-icon">🔊</span></span> 表和 <span class="term" data-pronunciation="肯派克神">Compaction<span class="pronun-icon">🔊</span></span> 机制优化存储和查询性能，实现流批一体查询和分钟级增量数据更新。</li>
                    
                    <li>负责 <span class="term" data-pronunciation="夫林克 C-D-C">Flink CDC<span class="pronun-icon">🔊</span></span> 全量+增量数据同步开发，实时同步 <span class="term" data-pronunciation="欧瑞口">Oracle<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="D-B-2">DB2<span class="pronun-icon">🔊</span></span>、<span class="term explain" data-pronunciation="迈思Q偶" data-explain="最流行的开源关系型数据库。

✦ 核心特点：
• 开源免费（社区版）
• 高性能：支持千万级数据
• ACID事务：保证数据一致性
• 主从复制：读写分离、高可用

✦ 存储引擎：
• InnoDB：支持事务，行级锁，默认引擎
• MyISAM：不支持事务，表级锁，已过时

✦ 优化要点：
• 索引：B+树，加速查询
• 慢查询优化：EXPLAIN分析
• 分库分表：应对大数据量
• 读写分离：主写从读

✦ 面试高频问题：
• 索引原理？→ B+树，叶子节点存数据
• 事务隔离级别？→ 读未提交/读已提交/可重复读/串行化
• 如何避免慢查询？→ 加索引、避免全表扫描
• 主从延迟怎么办？→ 并行复制、半同步复制">MySQL<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 等核心业务库数据到数据湖，支持 <span class="term" data-pronunciation="斯key马">Schema<span class="pronun-icon">🔊</span></span> 自动演进和断点续传，管理 TB 级状态数据，通过 <span class="term" data-pronunciation="若克斯-D-B">RocksDB<span class="pronun-icon">🔊</span></span> 增量 Checkpoint 保证大状态场景下的稳定性和容错恢复。</li>
                    
                    <li>负责 <span class="term explain" data-pronunciation="夫林克" data-explain="Apache Flink，实时流式计算引擎。

✦ 核心能力：
• 流批一体：同一套代码处理流数据和批数据
• 低延迟：毫秒级延迟，比Spark Streaming快
• 高吞吐：单机百万QPS
• Exactly-Once：数据不重不丢

✦ 核心概念：
• Checkpoint：状态快照，故障恢复
• Watermark：处理乱序数据的时间戳机制
• 状态管理：TB级状态数据（RocksDB）
• 反压：下游慢了自动降速

✦ vs Spark：
• Flink真流处理，Spark是微批
• Flink延迟更低（毫秒 vs 秒）
• Flink状态管理更强
• Spark生态更成熟

✦ 项目中的应用：
• 实时交易计算（5000万+笔/天）
• 实时数据入湖（Flink+Hudi）
• CDC数据同步（Oracle→数据湖）
• 实时风控（状态累计、规则触发）">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> <span class="term" data-pronunciation="内踢舞">Native<span class="pronun-icon">🔊</span></span> <span class="term explain" data-pronunciation="K8斯" data-explain="Kubernetes，容器编排平台，云原生基础设施。

✦ 核心功能：
• 自动调度：根据资源自动分配Pod
• 自愈：Pod挂了自动重启
• 弹性扩缩容：根据负载自动增减Pod
• 负载均衡：Service自动分发流量
• 滚动更新：零停机部署

✦ 核心概念：
• Pod：最小部署单元，包含1个或多个容器
• Service：Pod的访问入口，提供负载均衡
• Deployment：管理Pod副本数
• Namespace：资源隔离
• ConfigMap/Secret：配置管理

✦ 项目中的应用：
• Flink Native K8s：动态申请资源，用完释放
• AI服务部署：模型推理服务容器化
• 微服务编排：多个服务协同工作

✦ 面试要点：
• 为什么用K8s？→ 自动化运维、弹性扩缩容
• Pod和容器的区别？→ Pod是逻辑单元，可包含多个容器">K8s<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 部署与可观测性建设，实现作业动态扩缩容和故障自愈，搭建 <span class="term" data-pronunciation="破米修斯">Prometheus<span class="pronun-icon">🔊</span></span> + <span class="term" data-pronunciation="格拉发那">Grafana<span class="pronun-icon">🔊</span></span> 监控体系和 <span class="term" data-pronunciation="劳ging-啊婆瑞特">Logging-operator<span class="pronun-icon">🔊</span></span> 日志收集。</li>
                    
                    <li>参与后端系统开发工作，熟练运用 Java 技术栈（Spring Boot、Dubbo、<span class="term" data-pronunciation="迈百踢斯-泼拉斯">MyBatis-Plus<span class="pronun-icon">🔊</span></span>），为实时计算平台提供作业管理、监控告警、血缘分析等功能。</li>
                </ol>
            </div>

            <button class="collapsible">北京友友天宇系统技术有限公司 | 数据开发 | 2016.12-2021.11</button>
            <div class="collapsible-content">
                <ol>
                    <li>负责互联网舆情大数据分析平台的架构设计与核心开发，7x24小时实时采集论坛、微博、新闻等公开数据，通过 <span class="term" data-pronunciation="N-L-P">NLP<span class="pronun-icon">🔊</span></span> 技术进行情感分析、敏感词识别、热点挖掘，为政府部门和企业提供舆情预警、品牌监测、危机公关等决策支持。</li>
                    
                    <li>负责实时流式计算架构的设计与优化，前期基于 <span class="term" data-pronunciation="斯托姆">Storm<span class="pronun-icon">🔊</span></span> 构建实时处理流水线（2016-2018），后期主导技术升级迁移到 Flink（2018-2021），实现数据清洗、敏感词过滤、情感分析、地域分析、关键词提取等 NLP 处理，支持动态扩容和故障恢复。</li>
                    
                    <li>负责数据采集与存储架构设计，使用 <span class="term" data-pronunciation="夫卢姆">Flume<span class="pronun-icon">🔊</span></span> 实时监控文件变化、Kafka 分类分发数据流，MySQL 存储 7 天热点数据供实时展示，<span class="term" data-pronunciation="H-贝斯">HBase<span class="pronun-icon">🔊</span></span> 存储历史数据支持离线分析，通过 HBase <span class="term" data-pronunciation="肉key">RowKey<span class="pronun-icon">🔊</span></span> 优化设计提升查询性能 3 倍。</li>
                    
                    <li>负责离线数据分析与报表开发，使用 Hive + Spark SQL 进行各省负面信息、行业热点、品牌热度等多维度统计分析，通过 <span class="term" data-pronunciation="斯库破">Sqoop<span class="pronun-icon">🔊</span></span> 定时同步分析结果到 MySQL，支持 Web 报表展示和预警通知（邮件、短信、弹窗）。</li>
                    
                    <li>负责基于开源 <span class="term" data-pronunciation="H-D-P">HDP<span class="pronun-icon">🔊</span></span> 的大数据基础平台 KDP 的二次开发与维护（2020-2021），完成 x86/ARM 架构适配、国产数据库（达梦）元数据存储适配，利用 Spring Boot + Dubbo 开发后端业务系统，实现 Spark 作业的可视化界面管理。</li>
                </ol>
            </div>

            <hr>

            <h3>项目经历</h3>

            <button class="collapsible">项目一：SuperAgent 智能体平台 | 2025.04-至今</button>
            <div class="collapsible-content">
                <div class="highlight-box">
                    <h4>项目背景</h4>
                    <p>面向军工企业内网环境的通用智能体平台，基于 Google Agent2Agent Protocol (A2A) 协议，用户通过自然语言对话提出需求，决策智能体拆解任务并调度专业智能体协作完成，支持 PPT 生成、文档处理、代码执行、会议纪要等复杂任务，整个执行过程透明可视化。</p>
                    
                    <h4>技术栈</h4>
                    <ul>
                        <li><strong>大模型</strong>：Qwen2.5/Qwen3、DeepSeek-V2/V3、ChatGLM3/4</li>
                        <li><strong>推理框架</strong>：vLLM、Triton Inference Server</li>
                        <li><strong>硬件环境</strong>：华为昇腾 910B（8卡 × 64GB）</li>
                        <li><strong>后端</strong>：Spring Boot、Dubbo、MyBatis-Flex</li>
                        <li><strong>前端</strong>：Vue3、WebSocket、SSE</li>
                        <li><strong>基础设施</strong>：<span class="term explain" data-pronunciation="K8斯" data-explain="Kubernetes，容器编排平台，云原生基础设施。

✦ 核心功能：
• 自动调度：根据资源自动分配Pod
• 自愈：Pod挂了自动重启
• 弹性扩缩容：根据负载自动增减Pod
• 负载均衡：Service自动分发流量
• 滚动更新：零停机部署

✦ 核心概念：
• Pod：最小部署单元，包含1个或多个容器
• Service：Pod的访问入口，提供负载均衡
• Deployment：管理Pod副本数
• Namespace：资源隔离
• ConfigMap/Secret：配置管理

✦ 项目中的应用：
• Flink Native K8s：动态申请资源，用完释放
• AI服务部署：模型推理服务容器化
• 微服务编排：多个服务协同工作

✦ 面试要点：
• 为什么用K8s？→ 自动化运维、弹性扩缩容
• Pod和容器的区别？→ Pod是逻辑单元，可包含多个容器">K8s<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term explain" data-pronunciation="多克" data-explain="容器技术，轻量级虚拟化方案。

✦ 核心概念：
• 镜像(Image)：只读模板，包含代码+依赖
• 容器(Container)：镜像的运行实例
• 仓库(Registry)：存储镜像，如DockerHub

✦ vs 虚拟机：
• Docker更轻量：共享宿主机内核
• 启动快：秒级 vs 分钟级
• 资源占用少：MB级 vs GB级
• 隔离性较弱：共享内核

✦ 核心优势：
• 环境一致：开发、测试、生产环境完全一致
• 快速部署：秒级启动
• 易迁移：一次构建，到处运行
• 资源隔离：进程、网络、文件系统隔离

✦ 项目中的应用：
• 智能体任务隔离：每个任务独立容器
• 微服务部署：每个服务一个容器
• CI/CD：构建、测试、部署自动化

✦ 面试要点：
• Dockerfile：定义镜像构建步骤
• 数据卷：持久化数据
• 网络模式：bridge/host/none">Docker<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term explain" data-pronunciation="瑞迪斯" data-explain="内存数据库，Key-Value存储。

✦ 核心特点：
• 快：全内存操作，QPS 10万+
• 丰富数据结构：String、List、Set、Hash、ZSet
• 持久化：RDB快照+AOF日志
• 分布式：支持集群、主从复制

✦ 常用场景：
• 缓存：热点数据缓存，减轻DB压力
• 会话存储：用户Session
• 排行榜：ZSet实现
• 分布式锁：SETNX实现
• 消息队列：List/Stream

✦ vs Memcached：
• Redis数据结构丰富
• Redis支持持久化
• Redis单线程但性能更好（IO多路复用）

✦ 面试要点：
• 为什么快？→内存+单线程+IO多路复用
• 如何保证高可用？→主从+哨兵+集群">Redis<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></li>
                    </ul>
                    
                    <h4>核心工作</h4>
                    <ol>
                        <li>基于 Google A2A 协议实现多智能体协作框架</li>
                        <li>大模型本地化部署与 vLLM 推理优化（INT8 量化、FlashAttention2）</li>
                        <li>昇腾 910B 显卡适配（CANN 算子库、算子优化、显存管理）</li>
                        <li>Triton 统一推理平台构建（集成 vLLM + TensorRT）</li>
                        <li>容器化执行环境（Docker 沙箱）与可视化展示</li>
                    </ol>
                    
                    <h4>性能指标</h4>
                    <p>首字延迟 < 500ms，吞吐量 50+ 并发，系统响应延迟 < 3秒</p>
                </div>
            </div>

            <button class="collapsible">项目二：企业智能问答与数据分析平台 | 2024.09-2025.11</button>
            <div class="collapsible-content">
                <div class="highlight-box">
                    <h4>项目背景</h4>
                    <p>军工企业内网环境下的智能问答平台，整合文档、数据库、业务系统等多源数据，支持 <span class="term" data-pronunciation="拉格">RAG<span class="pronun-icon">🔊</span></span> 文档问答和 <span class="term" data-pronunciation="特克斯特兔思Q偶">Text2SQL<span class="pronun-icon">🔊</span></span> 自然语言查询。</p>
                    
                    <h4>技术栈</h4>
                    <ul>
                        <li><strong>大模型</strong>：Qwen2.5-14B、Qwen2.5-Coder-7B</li>
                        <li><strong>向量化</strong>：BGE-M3（1024维）、gte-Qwen2-7B（重排序）</li>
                        <li><strong>向量数据库</strong>：Milvus（HNSW索引）</li>
                        <li><strong>推理框架</strong>：vLLM</li>
                        <li><strong>数据库</strong>：MySQL、PostgreSQL、Oracle、ClickHouse</li>
                        <li><strong>后端</strong>：<span class="term explain" data-pronunciation="斯普灵布特" data-explain="简化Spring开发的快速开发框架。

✦ 核心优势：
• 约定优于配置：零配置快速启动
• 自动配置：根据依赖自动配置Bean
• 内嵌服务器：Tomcat/Jetty，无需部署WAR
• 生产级特性：监控、健康检查开箱即用

✦ 核心注解：
• @SpringBootApplication：启动类注解
• @RestController：RESTful接口
• @Autowired：自动注入
• @Service/@Repository：业务层/数据层
• @Configuration/@Bean：配置类

✦ 自动配置原理：
• @EnableAutoConfiguration触发
• 扫描META-INF/spring.factories
• 根据@Conditional条件判断
• 满足条件自动创建Bean

✦ 常用功能：
• 统一异常处理：@ControllerAdvice
• 参数校验：@Valid + @Validated
• AOP：@Aspect切面编程
• 事务：@Transactional

✦ 面试要点：
• 启动流程：7步骤（加载配置→创建容器→自动配置...）
• vs Spring：配置少、开发快、易部署">Spring Boot<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term explain" data-pronunciation="瑞迪斯" data-explain="内存数据库，Key-Value存储。

✦ 核心特点：
• 快：全内存操作，QPS 10万+
• 丰富数据结构：String、List、Set、Hash、ZSet
• 持久化：RDB快照+AOF日志
• 分布式：支持集群、主从复制

✦ 常用场景：
• 缓存：热点数据缓存，减轻DB压力
• 会话存储：用户Session
• 排行榜：ZSet实现
• 分布式锁：SETNX实现
• 消息队列：List/Stream

✦ vs Memcached：
• Redis数据结构丰富
• Redis支持持久化
• Redis单线程但性能更好（IO多路复用）

✦ 面试要点：
• 为什么快？→内存+单线程+IO多路复用
• 如何保证高可用？→主从+哨兵+集群">Redis<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>、<span class="term" data-pronunciation="森踢纳偶">Sentinel<span class="pronun-icon">🔊</span></span></li>
                    </ul>
                    
                    <h4>核心工作</h4>
                    <ol>
                        <li><span class="term" data-pronunciation="拉格">RAG<span class="pronun-icon">🔊</span></span> 文档问答：文档解析 → 切片 → 向量化 → 混合检索 → 重排序 → 生成答案</li>
                        <li><span class="term" data-pronunciation="特克斯特兔思Q偶">Text2SQL<span class="pronun-icon">🔊</span></span> 智能查询：<span class="term" data-pronunciation="斯key马">Schema<span class="pronun-icon">🔊</span></span> 向量化 + <span class="term" data-pronunciation="few秀特">Few-shot<span class="pronun-icon">🔊</span></span> + <span class="term" data-pronunciation="chen-of-扫特">Chain-of-Thought<span class="pronun-icon">🔊</span></span></li>
                        <li>向量检索优化：混合检索（向量 + BM25）、二阶段重排序</li>
                        <li>推理性能优化：模型量化、缓存机制、流式输出、限流降级</li>
                    </ol>
                    
                    <h4>性能指标</h4>
                    <p>检索延迟 < 50ms，首字延迟 < 500ms，Text2SQL 准确率：简单查询 95%+、复杂查询 80%+</p>
                </div>
            </div>

            <button class="collapsible">项目三：某中央银行实时数据湖平台 | 2021.12-2024.08</button>
            <div class="collapsible-content">
                <div class="highlight-box">
                    <h4>项目背景</h4>
                    <p>银行核心业务系统，承载交易计算、风控监测、监管报送等业务，日均处理 5000 万+ 笔交易流水、2TB+ 数据量。</p>
                    
                    <h4>技术栈</h4>
                    <ul>
                        <li><strong>实时计算</strong>：<span class="term explain" data-pronunciation="夫林克" data-explain="Apache Flink，实时流式计算引擎。

✦ 核心能力：
• 流批一体：同一套代码处理流数据和批数据
• 低延迟：毫秒级延迟，比Spark Streaming快
• 高吞吐：单机百万QPS
• Exactly-Once：数据不重不丢

✦ 核心概念：
• Checkpoint：状态快照，故障恢复
• Watermark：处理乱序数据的时间戳机制
• 状态管理：TB级状态数据（RocksDB）
• 反压：下游慢了自动降速

✦ vs Spark：
• Flink真流处理，Spark是微批
• Flink延迟更低（毫秒 vs 秒）
• Flink状态管理更强
• Spark生态更成熟

✦ 项目中的应用：
• 实时交易计算（5000万+笔/天）
• 实时数据入湖（Flink+Hudi）
• CDC数据同步（Oracle→数据湖）
• 实时风控（状态累计、规则触发）">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>（<span class="term" data-pronunciation="内踢舞">Native<span class="pronun-icon">🔊</span></span> <span class="term explain" data-pronunciation="K8斯" data-explain="Kubernetes，容器编排平台，云原生基础设施。

✦ 核心功能：
• 自动调度：根据资源自动分配Pod
• 自愈：Pod挂了自动重启
• 弹性扩缩容：根据负载自动增减Pod
• 负载均衡：Service自动分发流量
• 滚动更新：零停机部署

✦ 核心概念：
• Pod：最小部署单元，包含1个或多个容器
• Service：Pod的访问入口，提供负载均衡
• Deployment：管理Pod副本数
• Namespace：资源隔离
• ConfigMap/Secret：配置管理

✦ 项目中的应用：
• Flink Native K8s：动态申请资源，用完释放
• AI服务部署：模型推理服务容器化
• 微服务编排：多个服务协同工作

✦ 面试要点：
• 为什么用K8s？→ 自动化运维、弹性扩缩容
• Pod和容器的区别？→ Pod是逻辑单元，可包含多个容器">K8s<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>）、<span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></li>
                        <li><strong>数据湖</strong>：Hudi（COW/MOR）、HDFS</li>
                        <li><strong>数据同步</strong>：Flink CDC（Oracle、DB2、MySQL）</li>
                        <li><strong>存储</strong>：RocksDB、Redis、Oracle、HBase</li>
                        <li><strong>监控</strong>：Prometheus、Grafana、Logging-operator</li>
                    </ul>
                    
                    <h4>核心工作</h4>
                    <ol>
                        <li>Flink 实时计算开发：管理 TB 级状态数据，RocksDB 增量 Checkpoint</li>
                        <li>性能优化：Kafka 分区扩容（64→256）、Flink 并行度调优（32→128）、Checkpoint 优化（20min→5min）</li>
                        <li>实时数据湖架构：Flink 实时入湖到 HDFS，支持流批一体查询</li>
                        <li><span class="term" data-pronunciation="夫林克 C-D-C">Flink CDC<span class="pronun-icon">🔊</span></span> 全量+增量数据同步：<span class="term" data-pronunciation="斯key马">Schema<span class="pronun-icon">🔊</span></span> 自动演进、断点续传</li>
                        <li>K8s 部署与可观测性建设：动态扩缩容、故障自愈、监控告警</li>
                    </ol>
                    
                    <h4>优化效果</h4>
                    <p>处理延迟从小时级降到秒级，Checkpoint 成功率从 70% 提升到 99%+</p>
                </div>
            </div>

            <button class="collapsible">项目四：自研纯Java大数据平台 | 2021.01-2021.11</button>
            <div class="collapsible-content">
                <div class="highlight-box">
                    <h4>项目背景</h4>
                    <p>自研的纯 Java 云原生大数据集群管理平台，覆盖 Hadoop 生态的组件部署/管理/监控/自动化运维，支持多集群管理与国产化、多架构兼容（x86/ARM/国产OS），可观测与告警完善；亦支持裸机部署。</p>
                    
                    <h4>技术栈</h4>
                    <ul>
                        <li><strong>后端</strong>：Java、Spring Boot</li>
                        <li><strong>容器编排</strong>：K8s、Docker</li>
                        <li><strong>大数据组件</strong>：Hadoop、HDFS、Yarn、Hive、Spark、Ranger、Kerberos</li>
                        <li><strong>监控</strong>：Prometheus、Grafana</li>
                        <li><strong>国产化</strong>：达梦数据库、飞腾 CPU、ARM 架构</li>
                    </ul>
                    
                    <h4>核心工作</h4>
                    <ol>
                        <li>组件一键化管理：Kibana、Kyuubi、Tez、Zeppelin、Phoenix、Doris、StarRocks、Kafka Eagle、Logstash、JuiceFS 等</li>
                        <li>K8s 编排：按角色生成 ConfigMap/Secret/Service/PVC/Deployment/StatefulSet</li>
                        <li>安全治理：Ranger/KMS 策略下发、Kerberos Keytab 分发、达梦数据库适配</li>
                        <li>实时日志与运维：SSE 实时日志推送，安装/启动过程可视化，操作审计记录</li>
                    </ol>
                </div>
            </div>

            <button class="collapsible">项目五：大数据基础平台（HDP二开）| 2020.01-2020.12</button>
            <div class="collapsible-content">
                <div class="highlight-box">
                    <h4>项目背景</h4>
                    <p>基于开源 HDP（Hortonworks Data Platform）深度定制的企业级 Hadoop 集群管理平台，针对国产化环境进行全面适配改造，支持多架构部署和新一代大数据组件集成。</p>
                    
                    <h4>技术栈</h4>
                    <ul>
                        <li><strong>基础</strong>：HDP 3.x、Python、Java、C++、Shell</li>
                        <li><strong>大数据组件</strong>：Hadoop、Hive、Spark、HBase、Kafka、Flink 1.15+</li>
                        <li><strong>国产化</strong>：ARM 架构、达梦数据库、麒麟 V10、飞腾 CPU</li>
                        <li><strong>新组件</strong>：Kudu、Alluxio、MinIO、Iceberg、Impala</li>
                    </ul>
                    
                    <h4>核心工作</h4>
                    <ol>
                        <li>ARM 架构适配：完成 x86 到 ARM（飞腾）架构的全栈移植，JVM 调优和组件编译优化</li>
                        <li>达梦数据库适配：改造 Hive Metastore 支持达梦，解决 DDL 语法、数据类型映射、字符集兼容</li>
                        <li>ClickHouse 飞腾适配：源码级改造，针对 ARM 重新编译，优化 JNI 调用和内存管理</li>
                        <li>高版本组件集成：集成 Flink 1.15+、Kudu、Alluxio、MinIO、Iceberg 等新一代组件</li>
                    </ol>
                </div>
            </div>

            <button class="collapsible">项目六：舆情大数据分析系统 | 2016.12-2020.01</button>
            <div class="collapsible-content">
                <div class="highlight-box">
                    <h4>项目背景</h4>
                    <p>互联网舆情监控分析平台，7x24小时实时采集论坛、微博、新闻等公开数据，通过 NLP 技术进行情感分析、敏感词识别、热点挖掘，为政府部门和企业提供舆情预警、品牌监测、危机公关等决策支持。</p>
                    
                    <h4>技术栈</h4>
                    <ul>
                        <li><strong>实时计算</strong>：Storm（2016-2018）→ Flink（2018-2020）</li>
                        <li><strong>数据采集</strong>：Flume、Kafka</li>
                        <li><strong>存储</strong>：MySQL（7天热点）、HBase（历史数据）</li>
                        <li><strong>离线分析</strong>：Hive、Spark SQL、Sqoop</li>
                        <li><strong>NLP</strong>：jieba 分词、SnowNLP 情感分析、TextRank 关键词提取</li>
                    </ul>
                    
                    <h4>核心工作</h4>
                    <ol>
                        <li>实时流式计算架构设计与优化（Storm → Flink 技术升级）</li>
                        <li>NLP 实时处理：数据清洗、敏感词过滤、情感分析、地域分析、关键词提取</li>
                        <li>数据采集与存储架构：Flume 监控文件 → Kafka 分发 → MySQL/HBase</li>
                        <li>HBase RowKey 优化设计（地域 + 时间戳反转），查询性能提升 3 倍</li>
                        <li>离线数据分析与报表：Hive + Spark SQL 多维度统计，预警通知（邮件、短信、弹窗）</li>
                    </ol>
                </div>
            </div>

            <hr>

            <h3>教育背景</h3>
            <div class="tech-box">
                <p><strong>北京城市学院</strong> | 本科 | 计算机科学与技术 | 2013-2017</p>
            </div>
        </div>

        <div class="content" id="content1">
            <h2>🤖 AI 大模型技术详解</h2>
            
            <p class="warning-box">
                <strong>💡 使用提示：</strong><br>
                <span style="color: #667eea;">●蓝色词<span class="term" data-pronunciation="拼音">🔊</span></span>：英文读音<br>
                <span style="color: #ff6b35;">●<u>橙色下划线词</u><span class="explain" data-explain="这是术语解释示例，解释为什么、怎么做">❓</span></span>：重要概念解释（"为什么"、"怎么做"）<br>
                <span style="color: #667eea;">●<u style="color: #ff6b35;">同时有🔊和❓</u></span>：同时显示读音和解释<br>
                <em>鼠标悬停或点击查看提示</em>
            </p>

            <button class="collapsible">⭐ 华为昇腾 910B - 国产AI芯片（面试必问）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>华为自研的AI训练和推理芯片</p>
                    
                    <h4>硬件规格</h4>
                    <ul>
                        <li>每张卡：64G <span class="term" data-pronunciation="H-B-M-2">HBM2<span class="pronun-icon">🔊</span></span> 显存</li>
                        <li>典型配置：8 卡服务器（总显存 512G）</li>
                        <li>支持精度：<span class="term" data-pronunciation="F-P-16">FP16<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="印特8">INT8<span class="pronun-icon">🔊</span></span> 等</li>
                    </ul>

                    <h4>为什么要适配</h4>
                    <p>军工、政府等涉密项目要求使用国产芯片，不能用 <span class="term" data-pronunciation="恩维迪亚">NVIDIA<span class="pronun-icon">🔊</span></span> <span class="term" data-pronunciation="鸡屁由">GPU<span class="pronun-icon">🔊</span></span>。</p>
                </div>

                <div class="answer-box">
                    <h4>适配工作（面试重点）</h4>
                    <ol>
                        <li>
                            <strong><span class="term" data-pronunciation="看">CANN<span class="pronun-icon">🔊</span></span> 算子库</strong>（Compute Architecture for Neural Networks）
                            <p>昇腾的深度学习框架，类似 NVIDIA 的 <span class="term" data-pronunciation="苦达">CUDA<span class="pronun-icon">🔊</span></span>。需要把 vLLM/<span class="term" data-pronunciation="派桃吃">PyTorch<span class="pronun-icon">🔊</span></span> 的 CUDA 调用替换成 CANN 接口。</p>
                        </li>
                        <li>
                            <strong>算子优化</strong>
                            <p>针对 <span class="term" data-pronunciation="额腾神">Attention<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="麦特目">MatMul<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="雷儿诺姆">LayerNorm<span class="pronun-icon">🔊</span></span> 等关键算子做昇腾优化，利用昇腾的 AI Core、Vector Core 并行计算能力。</p>
                        </li>
                        <li>
                            <strong>显存管理</strong>
                            <p>适配 vLLM 的 <span class="term" data-pronunciation="佩知的腾神">PagedAttention<span class="pronun-icon">🔊</span></span> 到昇腾显存管理，优化显存分配策略。Qwen3-14B 模型 INT8 量化后单卡就能跑。</p>
                        </li>
                        <li>
                            <strong>兼容性测试</strong>
                            <p>确保模型输出精度和 NVIDIA 一致（误差 < 0.1%），推理性能基本持平。</p>
                        </li>
                    </ol>
                </div>

                <div class="example-box">
                    <h4>面试这么说</h4>
                    <p>"我们项目用的是华为昇腾 910B，每张卡 64G 显存，8 卡服务器。因为军工项目要求国产化，不能用 <span class="term" data-pronunciation="恩维迪亚">NVIDIA<span class="pronun-icon">🔊</span></span>。我们做了专门的适配工作，主要是基于昇腾的 <span class="term explain" data-pronunciation="C-A-N-N" data-explain="Compute Architecture for Neural Networks，华为昇腾AI计算架构。

✦ 是什么：
• 华为的CUDA替代品
• 支持昇腾AI处理器（910/310系列）
• 提供算子库、编译器、调试工具
• 类比：CANN之于昇腾 = CUDA之于NVIDIA

✦ 为什么要适配：
• 国产化要求（军工、金融等行业）
• 不依赖美国技术（规避制裁风险）
• 昇腾910B性价比高（同性能比A100便宜）

✦ 适配难点：
• 算子兼容：PyTorch有几千个算子，CANN未必都有
• 性能差异：需要针对昇腾架构优化
• 生态不完善：文档少、社区小
• 调试困难：报错信息不如CUDA清晰

✦ 项目中的做法：
• 替换CUDA调用→CANN算子
• 优化Attention、MatMul等关键算子
• 调整显存分配策略
• 实测性能达到A100的90%+">CANN<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 算子库，把 <span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span> 和 <span class="term explain" data-pronunciation="炮奇" data-explain="深度学习框架，Facebook开发，用Python写神经网络">PyTorch<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 底层的 <span class="term explain" data-pronunciation="酷达" data-explain="NVIDIA显卡的编程接口">CUDA<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 调用替换成 <span class="term" data-pronunciation="C-A-N-N">CANN<span class="pronun-icon">🔊</span></span> 接口，还针对 <span class="term" data-pronunciation="阿腾神">Attention<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="妹吹克斯莫">MatMul<span class="pronun-icon">🔊</span></span> 这些关键算子做了性能优化。适配后 <span class="term" data-pronunciation="Q问3-14B">Qwen3-14B<span class="pronun-icon">🔊</span></span> 模型 <span class="term" data-pronunciation="印特8">INT8<span class="pronun-icon">🔊</span></span> 量化单卡就能跑，推理性能和 <span class="term" data-pronunciation="恩维迪亚 A100">NVIDIA A100<span class="pronun-icon">🔊</span></span> 基本持平，首字延迟 500ms 以内。"</p>
                </div>

                <div class="warning-box">
                    <h4>常见追问</h4>
                    <p><strong>Q：昇腾和 NVIDIA 性能差距大吗？</strong></p>
                    <p>A：经过优化后基本持平。INT8 推理吞吐量能达到 NVIDIA A100 的 90%+，对业务场景完全够用。</p>
                    
                    <p><strong>Q：适配过程遇到过什么问题？</strong></p>
                    <p>A：主要是算子兼容性问题。有些 <span class="term explain" data-pronunciation="炮奇" data-explain="深度学习框架">PyTorch<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 算子在昇腾上没有对应实现，需要用 <span class="term" data-pronunciation="C-A-N-N">CANN<span class="pronun-icon">🔊</span></span> 提供的算子组合替代，或者自己写算子。还有就是显存分配策略要针对昇腾的架构做调整，才能充分利用 64G 显存。</p>
                </div>
            </div>

            <button class="collapsible">⭐ <span class="term" data-pronunciation="拉格">RAG<span class="pronun-icon">🔊</span></span> + 向量化原理（重点掌握）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>RAG 是什么</h4>
                    <p><span class="term" data-pronunciation="瑞粹我-奥格门踢的 贞呢瑞神">Retrieval-Augmented Generation<span class="pronun-icon">🔊</span></span>，检索增强生成</p>
                    <p><strong>核心思路：</strong>先从知识库检索相关文档，然后把文档和问题一起给大模型，让模型基于真实文档回答。</p>
                </div>

                <div class="example-box">
                    <h4>向量化原理（面试高频追问）</h4>
                    
                    <h4>1. 怎么把文本转成向量？</h4>
                    <p><strong>大白话：</strong>就像把文字拍照，变成"语义指纹"。这串数字不是随机的，而是编码了文本的语义信息。</p>
                    
                    <p><strong>技术原理：</strong></p>
                    <pre>第一步：分词（Tokenization）
"如何部署 Flink" 
   ↓ 分词
["如何", "部署", "Flink"]
   ↓ 转ID
[324, 1523, 4567]

第二步：通过神经网络编码
输入：Token IDs [324, 1523, 4567]
    ↓
经过多层 <span class="term" data-pronunciation="传思佛摸">Transformer<span class="pronun-icon">🔊</span></span>（12层或24层）
- 每层都在提取语义特征
- Self-Attention 捕捉词与词之间的关系
    ↓
输出：768 维向量 [0.23, -0.15, 0.88, ..., 0.42]</pre>

                    <p><strong>关键技术：</strong></p>
                    <ul>
                        <li><strong><span class="term" data-pronunciation="传思佛摸">Transformer<span class="pronun-icon">🔊</span></span> 架构</strong>：通过多头注意力机制理解词与词之间的关系</li>
                        <li><strong>预训练模型</strong>：BGE-M3、gte-Qwen2 这些模型已经在海量文本上训练过</li>
                        <li><strong>最后一层池化</strong>：把所有 Token 的向量聚合成一个句子级向量</li>
                    </ul>

                    <h4>2. 为什么相似的文本向量会很近？</h4>
                    <p><strong>大白话：</strong>因为模型是这么训练出来的！训练时，模型被"教"成：相似的句子 → 向量要靠近，不相似的句子 → 向量要离远。</p>
                    
                    <p><strong>技术原理：</strong>通过<strong>对比学习（Contrastive Learning）</strong>训练出来的。</p>
                    <pre>训练过程：
原句："如何部署 Flink"

正样本（相似）：
- "Flink 怎么安装"        ✅ 语义相似
- "Flink 部署教程"        ✅ 语义相似

负样本（不相似）：
- "今天天气真好"          ❌ 完全无关
- "Python 语法入门"       ❌ 完全无关

训练目标：
- 原句 和 正样本 → 余弦相似度 > 0.8（靠近）
- 原句 和 负样本 → 余弦相似度 < 0.2（远离）</pre>

                    <h4>3. 怎么衡量"近"和"远"？ - 余弦相似度</h4>
                    <pre>余弦相似度 = (向量A · 向量B) / (|A| × |B|)
结果范围：-1 到 1

  1.0  → 完全相同（0度夹角）
  0.8  → 很相似
  0.5  → 一般相似
  0.0  → 完全无关（90度夹角）
 -1.0  → 完全相反（180度夹角）

示例：
"如何部署 Flink"     → [0.2, 0.8, 0.3]
"Flink 怎么安装"     → [0.3, 0.7, 0.4]  
余弦相似度 = 0.92 ✅ 很近

"今天天气真好"       → [-0.5, 0.1, 0.9] 
余弦相似度 = 0.15 ❌ 很远</pre>

                    <h4>4. 百万级文档怎么做到毫秒级检索？- HNSW 索引</h4>
                    <p><strong>不是暴力遍历！</strong>我们用的是 <span class="term" data-pronunciation="H-N-S-W">HNSW<span class="pronun-icon">🔊</span></span>（分层可导航小世界图）索引算法。</p>
                    
                    <pre>通俗理解：
- 暴力搜索：像在字典里逐页找字（太慢）
- HNSW 索引：像在字典里先看拼音索引，快速定位（快几百倍）

查询过程：
用户查询向量
    ↓
从顶层开始，找最近的节点（粗定位）
    ↓
逐层向下，缩小范围
    ↓
到底层，精确找出 TopK（细查找）

效果对比：
- 暴力搜索：100 万向量需要计算 100 万次（几秒）
- HNSW 索引：只需要计算几百次（毫秒级）
- 准确率：99%+（几乎不损失准确性）</pre>
                </div>

                <div class="visualization-box">
                    <h4>🎯 RAG完整流程可视化</h4>
                    <div class="jvm-memory-demo">
                        <div class="tree-controls">
                            <button class="demo-btn" onclick="showRAGFlow()">🔄 RAG流程</button>
                            <button class="demo-btn" onclick="showVectorSearch()">🔍 向量检索</button>
                            <button class="demo-btn" onclick="resetRAGView()">🔄 重置</button>
                        </div>
                        
                        <div class="tree-canvas" id="ragCanvas">
                            <svg width="100%" height="500" viewBox="0 0 900 500">
                                <text x="450" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">RAG工作原理</text>
                                <text x="450" y="250" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细流程</text>
                            </svg>
                            <div class="tree-explanation">
                                <p id="ragExplanation">点击按钮查看RAG流程或向量检索原理</p>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="answer-box">
                    <h4>完整 RAG 流程</h4>
                    <ol>
                        <li><strong>文档解析</strong>：支持 PDF、Word、Excel、Markdown，用 <span class="term" data-pronunciation="P-D-F普拉母">PDFPlumber<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="安斯茶克吃的">Unstructured<span class="pronun-icon">🔊</span></span></li>
                        <li><strong><span class="explain" data-explain="为什么要切片？文档太长（几十页），一次全给模型：1.超token限制 2.无关信息太多影响准确率。切成小段，只给相关的几段">智能切片<span class="explain-icon">❓</span></span></strong>：按语义分段，每段 500-1000 字，<span class="explain" data-explain="为什么要overlap？比如一句话'Flink...性能好'被切成两段，overlap保证完整性，不会把重要信息切断">相邻段有 50 token overlap<span class="explain-icon">❓</span></span></li>
                        <li><strong>向量化</strong>：使用 BGE-M3 模型（1024 维向量）</li>
                        <li><strong>向量存储</strong>：存入 Milvus，建立 HNSW 索引</li>
                        <li><strong><span class="explain" data-explain="为什么混合检索？向量检索：语义相似但可能漏关键词。BM25：精确匹配关键词但不懂语义。两个一起用，召回更全面">混合检索<span class="explain-icon">❓</span></span></strong>：向量检索 + BM25 关键词检索</li>
                        <li><strong><span class="explain" data-explain="为什么要重排序？粗排（向量检索）速度快但不够准，可能把不太相关的也召回了。重排序用更精准的模型再打分，选出真正最相关的">重排序<span class="explain-icon">❓</span></span></strong>：用 gte-Qwen2-7B 精排，选出最相关的 3-5 篇</li>
                        <li><strong>生成答案</strong>：把文档和问题给大模型，生成答案并标注来源</li>
                    </ol>
                </div>

                <div class="example-box">
                    <h4>面试这么说</h4>
                    <p>"我们的智能问答平台基于 <span class="term" data-pronunciation="拉格">RAG<span class="pronun-icon">🔊</span></span> 架构。文本向量化是通过 <span class="term" data-pronunciation="传思佛摸">Transformer<span class="pronun-icon">🔊</span></span> 神经网络实现的，具体是 <span class="term" data-pronunciation="B-G-E M-3">BGE-M3<span class="pronun-icon">🔊</span></span> 模型，输出 1024 维向量。向量之间的相似度用余弦相似度衡量，范围是 -1 到 1，越接近 1 说明越相似。</p>
                    <p>为了实现百万级文档的毫秒级检索，我们用了 HNSW 索引算法，这是一种多层图结构，可以快速定位最相似的向量。整个检索过程包括向量检索 + BM25 关键词检索的混合召回，然后用 gte-Qwen2-7B 重排序模型精排，最终选出最相关的 3-5 篇文档给大模型生成答案。检索延迟控制在 50ms 以内。"</p>
                </div>
            </div>

            <button class="collapsible">Text2SQL - 自然语言转SQL（重点）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>用户说人话，系统自动生成 SQL 查询数据库</p>
                    
                    <h4>使用模型</h4>
                    <p>Qwen2.5-Coder-7B（专门针对代码优化）</p>
                </div>

                <div class="answer-box">
                    <h4>实现方案（面试重点）</h4>
                    <ol>
                        <li><strong><span class="term" data-pronunciation="斯key马">Schema<span class="pronun-icon">🔊</span></span> 向量化</strong>：让模型知道数据库有哪些表、哪些字段</li>
                        <li><strong>Few-shot 示例</strong>：给几个示例，教模型怎么写 SQL</li>
                        <li><strong>Chain-of-Thought</strong>：让模型一步步推理生成 SQL</li>
                        <li><strong>安全校验</strong>：禁止 DROP、DELETE 等危险操作，自动加 LIMIT</li>
                    </ol>

                    <h4>准确率</h4>
                    <ul>
                        <li>简单查询（单表、简单条件）：95%+</li>
                        <li>复杂查询（多表关联、复杂聚合）：80%+</li>
                    </ul>
                </div>

                <div class="example-box">
                    <h4>应对面试官追问：处理什么数据格式？</h4>
                    <p><strong>输入数据格式：</strong></p>
                    <ul>
                        <li>数据库表结构（JSON 格式）：包含表名、字段名、字段类型、字段说明</li>
                        <li>用户自然语言查询</li>
                        <li>Few-shot 示例（相似问题的 SQL）</li>
                    </ul>

                    <p><strong>输出数据格式：</strong></p>
                    <ul>
                        <li>标准 SQL 语句（支持 MySQL、PostgreSQL、Oracle 等方言）</li>
                        <li>执行结果（JSON/表格）</li>
                        <li>可视化图表数据</li>
                    </ul>

                    <p><strong>数据处理流程：</strong></p>
                    <pre>1. <span class="term" data-pronunciation="斯key马">Schema<span class="pronun-icon">🔊</span></span> 预处理：
   - 提取表结构 → 转成自然语言描述 → 向量化
   
2. Query 理解：
   - 意图识别（查询/统计/分析）
   - 实体抽取（表名、字段名、条件值）
   
3. SQL 生成：
   - 模型推理 → 生成 SQL
   - 语法校验 → 安全检查
   
4. 执行与优化：
   - 执行 SQL → 获取结果
   - 结果聚合 → 生成图表</pre>
                </div>
            </div>

            <button class="collapsible">vLLM - 大模型推理框架</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>开源的大语言模型推理加速引擎</p>
                    
                    <h4>核心技术</h4>
                    <ul>
                        <li><strong>PagedAttention</strong>：像操作系统管理内存一样管理显存，显存利用率提升</li>
                        <li><strong>Continuous Batching</strong>：动态合并多个请求一起处理，吞吐量提升 2-3 倍</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们用 <span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span> 部署了 <span class="term" data-pronunciation="Q问3-14B">Qwen3-14B<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="地破席克-V3">DeepSeek-V3<span class="pronun-icon">🔊</span></span> 这些大模型。相比原生的 <span class="term explain" data-pronunciation="炮奇" data-explain="深度学习框架，Meta(Facebook)开发。

✦ 核心特点：
• 动态计算图：写代码像写Python，直观易懂
• 生态完善：大模型首选（Qwen/DeepSeek/LLaMA都基于它）
• GPU加速：底层调用CUDA，充分利用显卡算力
• 调试友好：报错信息清晰，方便排查问题

✦ vs TensorFlow：
• PyTorch更灵活，适合研究和快速迭代
• TensorFlow更适合大规模生产部署
• 现在AI领域PyTorch占主流（70%+）

✦ 项目中的作用：
• 模型加载和权重管理
• vLLM底层基于PyTorch
• 国产化时需要适配（CUDA→CANN）">PyTorch<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 推理，<span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span> 的吞吐量能提升好几倍。</p>
                    <p>它的核心优势有两个：一是 PagedAttention，把 KV Cache 像操作系统分页内存一样管理，显存利用率高；二是 Continuous Batching，来一个请求就处理一个，动态合并，不用等 batch 凑齐。</p>
                    <p>而且 <span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span> 提供 <span class="term" data-pronunciation="欧噗恩诶爱">OpenAI<span class="pronun-icon">🔊</span></span> 兼容的 <span class="term" data-pronunciation="诶批爱">API<span class="pronun-icon">🔊</span></span>，调用起来特别方便。我们做了 <span class="term" data-pronunciation="印特8">INT8<span class="pronun-icon">🔊</span></span> 量化，显存占用减半，首字延迟控制在 500ms 以内。"</p>
                </div>

                <div class="example-box">
                    <h4>部署架构</h4>
                    <ul>
                        <li><strong>单机多卡</strong>：一个 vLLM 实例管理一台服务器的多个 GPU（比如 8 卡）</li>
                        <li><strong>多机扩展</strong>：每台服务器独立运行一个 vLLM 实例，外部用 K8s Service 做负载均衡</li>
                        <li><strong>部署方式</strong>：Docker 容器化部署，K8s 编排</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">Triton Inference Server - 统一推理平台</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>NVIDIA 开源的统一 AI 模型推理服务器</p>
                    
                    <h4>核心特点</h4>
                    <ul>
                        <li>统一管理多种模型（大模型、向量模型、图像模型）</li>
                        <li>支持多种推理引擎（<span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="腾色-阿-踢">TensorRT<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="炮奇">PyTorch<span class="pronun-icon">🔊</span></span>）</li>
                        <li>自动负载均衡</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们用 Triton 构建了统一推理平台。把 vLLM 引擎（跑 Qwen3 大模型）、TensorRT 引擎（跑 BGE-M3 向量模型）都集成在 Triton 里，对外就是一个统一的接口。</p>
                    <p>这样做的好处是 GPU 资源可以统一调度，比如对话模型不忙的时候，显存就可以给向量模型用，资源利用率更高。我们通过 K8s 部署了多个 Triton 实例，每个实例管理一台服务器的 GPU，K8s Service 做负载均衡实现集群化服务。"</p>
                </div>
            </div>

            <button class="collapsible">向量检索与重排序</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>向量模型</h4>
                    <p><strong>BGE-M3</strong>（智源开源）：把文本转成 1024 维向量，支持中英文长文本（最长 8192 token）</p>
                    <p><strong>gte-Qwen2-7B</strong>：重排序模型，对检索结果进行精排</p>
                </div>

                <div class="answer-box">
                    <h4>两阶段检索流程</h4>
                    <ol>
                        <li><strong>粗排（召回）</strong>：
                            <ul>
                                <li>向量检索：BGE-M3 召回 Top20</li>
                                <li>BM25 关键词检索：召回 Top20</li>
                                <li>合并去重：得到 30-40 个候选文档</li>
                            </ul>
                        </li>
                        <li><strong>精排（重排序）</strong>：
                            <ul>
                                <li>用 gte-Qwen2-7B 对候选文档重新打分</li>
                                <li>选出最相关的 3-5 篇文档</li>
                                <li>给大模型生成答案</li>
                            </ul>
                        </li>
                    </ol>
                </div>

                <div class="example-box">
                    <h4>应对面试官追问：用什么算法？</h4>
                    <ul>
                        <li><strong>HNSW</strong>（Hierarchical Navigable Small World）：多层图结构，快速近似最近邻搜索</li>
                        <li><strong>BM25</strong>（Best Matching 25）：基于词频的关键词检索算法</li>
                        <li><strong>余弦相似度</strong>：向量距离度量，范围 -1 到 1</li>
                        <li><strong><span class="term" data-pronunciation="传思佛摸">Transformer<span class="pronun-icon">🔊</span></span></strong>：重排序模型的神经网络架构</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">大模型系列</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>Qwen（通义千问）- 阿里开源</h4>
                    <ul>
                        <li><strong>Qwen2.5 / Qwen3</strong>：通用对话模型，中文能力强</li>
                        <li><strong>Qwen-Coder</strong>：专门针对代码优化，生成代码、SQL 特别准</li>
                        <li><strong>特点</strong>：开源、商用友好、中文好、推理速度快</li>
                    </ul>

                    <h4>DeepSeek（深度求索）- 开源</h4>
                    <ul>
                        <li><strong>DeepSeek-V2 / V3</strong>：性能很强的开源模型，推理能力强</li>
                        <li><strong>特点</strong>：MoE 架构（混合专家模型），成本低、效果好</li>
                    </ul>

                    <h4>ChatGLM（智谱 AI）- 开源</h4>
                    <ul>
                        <li><strong>ChatGLM3 / 4</strong>：中文对话模型，比较轻量</li>
                        <li><strong>特点</strong>：可以本地部署，显存占用相对小</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们项目里根据任务类型选择不同模型。通用对话用 Qwen3-14B，代码生成和 SQL 生成用 Qwen3-Coder-7B，复杂推理用 DeepSeek-V3。</p>
                    <p>而且经历过几次模型升级，比如 2025 年 5 月从 <span class="term" data-pronunciation="Q问2.5">Qwen2.5<span class="pronun-icon">🔊</span></span> 升级到 <span class="term" data-pronunciation="Q问3">Qwen3<span class="pronun-icon">🔊</span></span>，任务准确性明显提升。升级过程包括模型权重转换、<span class="term" data-pronunciation="撇破母特">Prompt<span class="pronun-icon">🔊</span></span> 模板适配、推理参数调优（temperature、top_p 等）。"</p>
                </div>
            </div>

            <button class="collapsible">模型优化技术</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>PagedAttention</h4>
                    <p><strong>作用：</strong>优化显存管理，像操作系统分页内存一样管理 KV Cache</p>
                    <p><strong>效果：</strong>显存利用率提升，能服务更多并发请求</p>

                    <h4>Continuous Batching</h4>
                    <p><strong>作用：</strong>动态合并请求，不用等一个 batch 凑齐才开始</p>
                    <p><strong>效果：</strong>吞吐量提升 2-3 倍</p>

                    <h4>FlashAttention2</h4>
                    <p><strong>作用：</strong>优化注意力机制计算，速度更快、显存更省</p>
                    <p><strong>效果：</strong>推理速度提升 20-30%</p>

                    <h4>模型量化（INT8/INT4）</h4>
                    <p><strong>作用：</strong>把 FP16 的模型参数转成 INT8，精度略降但速度和显存大幅优化</p>
                    <p><strong>效果：</strong>显存减半，推理速度提升 1.5-2 倍</p>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们用了多种优化技术。vLLM 自带的 PagedAttention 和 Continuous Batching 大幅提升了并发能力，FlashAttention2 加速了推理，INT8 量化让显存占用减半。</p>
                    <p>综合下来，首字延迟控制在 500ms 以内，吞吐量是原生 <span class="term explain" data-pronunciation="炮奇" data-explain="深度学习框架，Meta(Facebook)开发。

✦ 核心特点：
• 动态计算图：写代码像写Python，直观易懂
• 生态完善：大模型首选（Qwen/DeepSeek/LLaMA都基于它）
• GPU加速：底层调用CUDA，充分利用显卡算力
• 调试友好：报错信息清晰，方便排查问题

✦ vs TensorFlow：
• PyTorch更灵活，适合研究和快速迭代
• TensorFlow更适合大规模生产部署
• 现在AI领域PyTorch占主流（70%+）

✦ 项目中的作用：
• 模型加载和权重管理
• vLLM底层基于PyTorch
• 国产化时需要适配（CUDA→CANN）">PyTorch<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 的 3-4 倍。在昇腾 910B 显卡上，经过优化后推理性能能达到 <span class="term" data-pronunciation="恩维迪亚 A100">NVIDIA A100<span class="pronun-icon">🔊</span></span> 的 90%+。"</p>
                </div>
            </div>

            <button class="collapsible">⭐ PyTorch / CUDA / FlashAttention - AI基础技术（高频考点）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4><span class="term explain" data-pronunciation="炮奇" data-explain="深度学习框架，Meta(Facebook)开发。

✦ 核心特点：
• 动态计算图：写代码像写Python，直观易懂
• 生态完善：大模型首选（Qwen/DeepSeek/LLaMA都基于它）
• GPU加速：底层调用CUDA，充分利用显卡算力
• 调试友好：报错信息清晰，方便排查问题

✦ vs TensorFlow：
• PyTorch更灵活，适合研究和快速迭代
• TensorFlow更适合大规模生产部署
• 现在AI领域PyTorch占主流（70%+）

✦ 项目中的作用：
• 模型加载和权重管理
• vLLM底层基于PyTorch
• 国产化时需要适配（CUDA→CANN）">PyTorch<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> - 深度学习框架</h4>
                    <p><strong>是什么：</strong>Facebook（Meta）开发的深度学习框架，用 <span class="term" data-pronunciation="派森">Python<span class="pronun-icon">🔊</span></span> 编写神经网络。</p>
                    
                    <p><strong>为什么用它：</strong></p>
                    <ul>
                        <li><strong>简单易用</strong>：API 设计友好，上手快</li>
                        <li><strong>动态图</strong>：支持动态计算图，调试方便（vs TensorFlow 的静态图）</li>
                        <li><strong>生态丰富</strong>：大部分开源大模型（Qwen、DeepSeek、LLaMA）都基于 PyTorch</li>
                        <li><strong>GPU 加速</strong>：底层调用 CUDA，充分利用 GPU 算力</li>
                    </ul>
                    
                    <p><strong>实际项目中的作用：</strong></p>
                    <ul>
                        <li>模型训练框架（如果你做过微调）</li>
                        <li>模型推理的基础（vLLM 底层也是基于 PyTorch）</li>
                        <li>模型加载、权重管理</li>
                    </ul>
                </div>

                <div class="tech-box">
                    <h4><span class="term explain" data-pronunciation="酷达" data-explain="NVIDIA GPU的并行计算平台和编程接口。

✦ 为什么需要它：
• 并行计算：GPU有数千个核心，同时计算数千个数据
• 矩阵运算：深度学习全是矩阵乘法，GPU天生适合
• 显存管理：负责数据从内存→显存的搬运
• 比CPU快100-1000倍（矩阵运算场景）

✦ 国产化适配问题：
• 华为昇腾：用CANN替代CUDA
• 寒武纪：用BANG替代
• 主要难点：算子兼容性（CUDA有几千个算子）
• 性能调优：需要针对国产架构重新优化

✦ 面试要点：
• 为什么深度学习必须用GPU？→ 并行矩阵运算
• 国产化怎么适配？→ 算子映射+性能调优">CUDA<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> - GPU 编程平台</h4>
                    <p><strong>是什么：</strong>NVIDIA 显卡的并行计算平台和编程接口。想让 GPU 跑深度学习，底层必须用 CUDA。</p>
                    
                    <p><strong>为什么需要它：</strong></p>
                    <ul>
                        <li><strong>并行计算</strong>：GPU 有几千个计算核心，可以同时算几千个数据，比 CPU 快几百倍</li>
                        <li><strong>矩阵运算</strong>：深度学习全是矩阵乘法，GPU 天然适合</li>
                        <li><strong>显存管理</strong>：CUDA 负责把数据从内存搬到显存，管理 GPU 资源</li>
                    </ul>
                    
                    <p><strong>国产化适配问题：</strong></p>
                    <p>华为昇腾、寒武纪等国产芯片不支持 CUDA，需要适配：</p>
                    <ul>
                        <li><strong>昇腾</strong>：用 <span class="term explain" data-pronunciation="C-A-N-N" data-explain="Compute Architecture for Neural Networks，华为昇腾AI计算架构。

✦ 是什么：
• 华为的CUDA替代品
• 支持昇腾AI处理器（910/310系列）
• 提供算子库、编译器、调试工具
• 类比：CANN之于昇腾 = CUDA之于NVIDIA

✦ 为什么要适配：
• 国产化要求（军工、金融等行业）
• 不依赖美国技术（规避制裁风险）
• 昇腾910B性价比高（同性能比A100便宜）

✦ 适配难点：
• 算子兼容：PyTorch有几千个算子，CANN未必都有
• 性能差异：需要针对昇腾架构优化
• 生态不完善：文档少、社区小
• 调试困难：报错信息不如CUDA清晰

✦ 项目中的做法：
• 替换CUDA调用→CANN算子
• 优化Attention、MatMul等关键算子
• 调整显存分配策略
• 实测性能达到A100的90%+">CANN<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>（华为的 CUDA 替代品）</li>
                        <li><strong>寒武纪</strong>：用 BANG（寒武纪的计算库）</li>
                        <li><strong>适配工作</strong>：把 PyTorch 的 CUDA 调用替换成对应的国产接口</li>
                    </ul>
                </div>

                <div class="tech-box">
                    <h4><span class="term explain" data-pronunciation="夫拉虚腾神2" data-explain="斯坦福大学开发的Attention加速算法。

✦ 解决什么问题：
• Attention计算量O(n²)，序列越长越慢
• 显存占用大，限制上下文长度
• 传统方法要把整个矩阵加载到显存

✦ 核心优化：
• 分块计算：矩阵切成小块，每次只算一块
• Kernel Fusion：多个GPU操作合并，减少显存读写
• 利用SRAM：充分利用GPU的L1/L2缓存（比HBM快20倍）
• 数学等价：结果和原始Attention完全一样

✦ 实际效果：
• 速度提升2-5倍（序列越长提升越明显）
• 显存节省10-20倍
• 支持更长上下文（2K→32K→128K）

✦ 项目中的作用：
• vLLM默认集成FlashAttention2
• 推理速度提升，降低成本">FlashAttention2<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> - Attention 加速算法</h4>
                    <p><strong>是什么：</strong>斯坦福大学开发的 Attention 计算加速算法，让大模型推理快 3-5 倍。</p>
                    
                    <p><strong>为什么需要它：</strong></p>
                    <ul>
                        <li><strong>Attention 很慢</strong>：Transformer 模型的核心是 Attention，计算量是 O(n²)，序列越长越慢</li>
                        <li><strong>显存瓶颈</strong>：传统 Attention 要把整个矩阵加载到显存，占用大量空间</li>
                        <li><strong>FlashAttention 优化</strong>：
                            <ul>
                                <li>分块计算：把矩阵切成小块，每次只算一块</li>
                                <li>Kernel Fusion：多个 GPU 操作合并成一个，减少显存读写</li>
                                <li>充分利用 GPU 缓存（SRAM）</li>
                            </ul>
                        </li>
                    </ul>
                    
                    <p><strong>实际效果：</strong></p>
                    <ul>
                        <li>推理速度提升 2-5 倍</li>
                        <li>显存占用减少 10-20 倍</li>
                        <li>支持更长的上下文（从 2K 扩展到 32K）</li>
                    </ul>
                </div>

                <div class="tech-box">
                    <h4><span class="term explain" data-pronunciation="配秀特腾神" data-explain="vLLM的核心技术，像操作系统管理内存一样管理KV Cache。

✦ 传统方式的问题：
• 每个请求预分配固定显存（如2048 tokens）
• 实际可能只用500 tokens，浪费1500
• 显存利用率只有20-40%
• 并发量受限

✦ PagedAttention创新：
• 把KV Cache分成固定大小的"页"（如16 tokens/页）
• 动态分配：需要就分配，不需要就释放
• 非连续存储：页可以分散在显存各处
• 类比：就像虚拟内存的分页机制

✦ 实际效果：
• 显存利用率80%+（提升2-4倍）
• 同显存下并发量提升2-3倍
• 支持动态Batch：请求来一个处理一个
• 成本降低50%+

✦ 面试要点：
• 核心思想：分页管理
• 对比：传统预分配 vs 动态分配">PagedAttention<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> - vLLM 核心技术</h4>
                    <p><strong>是什么：</strong>vLLM 的核心技术，把 KV Cache 像操作系统管理内存一样分页管理。</p>
                    
                    <p><strong>解决什么问题：</strong></p>
                    <ul>
                        <li><strong>传统方式浪费显存</strong>：每个请求预先分配固定大小的显存，实际用不满，浪费严重</li>
                        <li><strong>PagedAttention 优化</strong>：
                            <ul>
                                <li>把 KV Cache 分成固定大小的"页"（类似操作系统的内存分页）</li>
                                <li>哪个请求需要，就动态分配页</li>
                                <li>请求结束，立即释放</li>
                            </ul>
                        </li>
                    </ul>
                    
                    <p><strong>实际效果：</strong></p>
                    <ul>
                        <li>显存利用率从 20-40% 提升到 80%+</li>
                        <li>同等显存下，并发量提升 2-3 倍</li>
                        <li>支持动态 Batch，请求来一个处理一个</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们项目基于 <span class="term explain" data-pronunciation="炮奇" data-explain="深度学习框架，Meta(Facebook)开发。

✦ 核心特点：
• 动态计算图：写代码像写Python，直观易懂
• 生态完善：大模型首选（Qwen/DeepSeek/LLaMA都基于它）
• GPU加速：底层调用CUDA，充分利用显卡算力
• 调试友好：报错信息清晰，方便排查问题

✦ vs TensorFlow：
• PyTorch更灵活，适合研究和快速迭代
• TensorFlow更适合大规模生产部署
• 现在AI领域PyTorch占主流（70%+）

✦ 项目中的作用：
• 模型加载和权重管理
• vLLM底层基于PyTorch
• 国产化时需要适配（CUDA→CANN）">PyTorch<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 框架，底层调用 <span class="term explain" data-pronunciation="酷达" data-explain="NVIDIA显卡的编程接口。想让GPU算得快，就得用CUDA写代码">CUDA<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 做 GPU 加速。推理引擎用 <span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span>，集成了 <span class="term explain" data-pronunciation="夫拉虚腾神2" data-explain="加速Attention计算的算法，速度提升3-5倍">FlashAttention2<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 和 <span class="term explain" data-pronunciation="配秀特腾神" data-explain="vLLM核心技术，把KV Cache分页管理，显存利用率提升80%+">PagedAttention<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 技术。</p>
                    <p>因为国产化要求，我们还适配了华为昇腾芯片，把 <span class="term" data-pronunciation="酷达">CUDA<span class="pronun-icon">🔊</span></span> 调用替换成 <span class="term explain" data-pronunciation="C-A-N-N" data-explain="Compute Architecture for Neural Networks，华为昇腾AI计算架构。

✦ 是什么：
• 华为的CUDA替代品
• 支持昇腾AI处理器（910/310系列）
• 提供算子库、编译器、调试工具
• 类比：CANN之于昇腾 = CUDA之于NVIDIA

✦ 为什么要适配：
• 国产化要求（军工、金融等行业）
• 不依赖美国技术（规避制裁风险）
• 昇腾910B性价比高（同性能比A100便宜）

✦ 适配难点：
• 算子兼容：PyTorch有几千个算子，CANN未必都有
• 性能差异：需要针对昇腾架构优化
• 生态不完善：文档少、社区小
• 调试困难：报错信息不如CUDA清晰

✦ 项目中的做法：
• 替换CUDA调用→CANN算子
• 优化Attention、MatMul等关键算子
• 调整显存分配策略
• 实测性能达到A100的90%+">CANN<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 接口，适配后推理性能达到 <span class="term" data-pronunciation="恩维迪亚">NVIDIA<span class="pronun-icon">🔊</span></span> 的 90%+。"</p>
                </div>

                <div class="warning-box">
                    <h4>常见追问</h4>
                    <p><strong>Q：PyTorch 和 TensorFlow 有什么区别？</strong></p>
                    <p>A：详见对比表格↓</p>
                    
                    <h4>⭐ PyTorch vs TensorFlow 全面对比</h4>
                    <table>
                        <tr>
                            <th>维度</th>
                            <th>PyTorch</th>
                            <th>TensorFlow</th>
                        </tr>
                        <tr>
                            <td><strong>计算图</strong></td>
                            <td>动态图（Define-by-Run）⭐<br>运行时构建，灵活</td>
                            <td>静态图（Define-and-Run）<br>TF2.0支持动态图</td>
                        </tr>
                        <tr>
                            <td><strong>易用性</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>代码简洁，接近Python</td>
                            <td>⭐⭐⭐<br>TF1.x复杂，TF2.x改进</td>
                        </tr>
                        <tr>
                            <td><strong>调试</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>直接用Python调试器</td>
                            <td>⭐⭐⭐<br>需要Session，调试困难</td>
                        </tr>
                        <tr>
                            <td><strong>生态</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>研究界首选（70%+论文）<br>大模型基本都用PyTorch</td>
                            <td>⭐⭐⭐⭐<br>工业界强，Google支持<br>TensorFlow Lite移动端好</td>
                        </tr>
                        <tr>
                            <td><strong>部署</strong></td>
                            <td>⭐⭐⭐<br>TorchScript、ONNX</td>
                            <td>⭐⭐⭐⭐⭐<br>TF Serving、TF Lite完善</td>
                        </tr>
                        <tr>
                            <td><strong>性能</strong></td>
                            <td>⭐⭐⭐⭐<br>研究场景性能好</td>
                            <td>⭐⭐⭐⭐<br>生产优化更好</td>
                        </tr>
                        <tr>
                            <td><strong>社区</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>增长快，活跃</td>
                            <td>⭐⭐⭐⭐<br>成熟但增长放缓</td>
                        </tr>
                        <tr>
                            <td><strong>适用场景</strong></td>
                            <td>• 研究和快速迭代⭐<br>• 大模型训练<br>• 需要灵活性的项目</td>
                            <td>• 大规模生产部署<br>• 移动端/嵌入式<br>• Google生态</td>
                        </tr>
                        <tr>
                            <td><strong>代表项目</strong></td>
                            <td>Qwen、DeepSeek、LLaMA<br>GPT、BERT、Stable Diffusion</td>
                            <td>TensorFlow Hub<br>Keras、TF Serving</td>
                        </tr>
                    </table>
                    
                    <p><strong>结论：</strong>2024年AI领域PyTorch占主导地位（尤其大模型），TensorFlow在生产部署和移动端仍有优势。</p>
                    
                    <p><strong>Q：FlashAttention 和 PagedAttention 有什么区别？</strong></p>
                    <p>A：FlashAttention 是优化 Attention 计算速度的算法，PagedAttention 是优化显存管理的技术。两者可以同时使用，vLLM 就同时集成了这两个技术。</p>
                    
                    <p><strong>Q：国产芯片适配难在哪？</strong></p>
                    <p>A：主要难在算子兼容性。CUDA 有几千个算子，国产芯片不一定都实现了。有些算子需要用多个国产算子组合替代，有些甚至要自己写。还有性能调优，需要针对国产芯片的架构特点重新优化。</p>
                </div>
            </div>

            <button class="collapsible">其他AI技术</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4><span class="term explain" data-pronunciation="诶兔诶" data-explain="Agent-to-Agent Protocol，Google提出的智能体通信标准。

✦ 解决什么问题：
• 多个智能体如何协作？
• 任务如何拆解和分配？
• 结果如何汇总？
• 不同智能体如何互通？

✦ 核心功能：
• 消息格式：定义标准的JSON消息结构
• 任务分发：主Agent→子Agent传递任务
• 状态同步：实时了解各Agent执行状态
• 结果聚合：收集并整合各Agent的输出

✦ 项目中的应用：
• SuperAgent平台基于A2A协议
• 支持PPT生成、数据分析、代码执行等复杂任务
• 任务自动拆解成多个子任务
• 多个Agent并行执行，提升效率

✦ 面试要点：
• 核心：定义智能体间的通信规范
• 类比：就像HTTP协议定义网页通信">A2A<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 协议（Agent-to-Agent Protocol）</h4>
                    <p>Google 提出的智能体之间通信的标准协议。定义了智能体之间怎么发消息、怎么传任务、怎么返回结果。</p>

                    <h4><span class="term explain" data-pronunciation="可哥VLM" data-explain="清华大学开发的视觉-语言多模态大模型。

✦ 核心能力：
• 看图说话：识别图片内容并生成描述
• 图表分析：理解柱状图、折线图、饼图等
• OCR增强：识别图片中的文字
• 视觉推理：基于图片内容回答问题

✦ 技术特点：
• 视觉编码器：处理图片特征
• 语言模型：生成文字描述
• 多模态融合：图片+文字联合理解
• 支持高分辨率图片（1344×1344）

✦ 项目中的应用：
• 数据可视化建议：分析图表，提供优化建议
• 报告自动生成：看图表自动写分析报告
• 图片内容检索：根据图片内容搜索
• 趋势分析：识别图表中的上升/下降趋势

✦ 面试要点：
• 多模态：视觉+语言的结合
• 实用场景：BI报表分析、文档理解">CogVLM<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> - 多模态模型</h4>
                    <p>视觉-语言多模态模型，能"看图说话"。用于图表识别、数据可视化建议、趋势分析等。</p>

                    <h4><span class="term" data-pronunciation="放-A-S-R">FunASR<span class="pronun-icon">🔊</span></span> - 语音识别</h4>
                    <p>达摩院开源的语音识别模型，支持实时流式识别。我们用 <span class="term" data-pronunciation="趴拉佛么">Paraformer<span class="pronun-icon">🔊</span></span> 模型做语音识别，通过 <span class="term" data-pronunciation="外柏搜ket">WebSocket<span class="pronun-icon">🔊</span></span> 实时传输音频流。</p>
                </div>
            </div>
        </div>

        <div class="content" id="content2">
            <h2>💾 大数据技术详解</h2>

            <p class="warning-box">
                <strong>💡 使用提示：</strong><br>
                <span style="color: #667eea;">●蓝色词<span class="term" data-pronunciation="拼音">🔊</span></span>：英文读音<br>
                <span style="color: #ff6b35;">●<u>橙色下划线词</u><span class="explain" data-explain="术语解释示例">❓</span></span>：技术组合原因、优化原理<br>
                <em>鼠标悬停或点击查看</em>
            </p>

            <button class="collapsible">Flink - 实时流式计算引擎（核心技能）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>实时流式计算引擎。数据像水流一样源源不断过来（比如 Kafka 的消息），Flink 实时处理这些数据，做统计、聚合、关联等操作，结果也是实时出来的。</p>
                    
                    <h4>核心概念</h4>
                    <ul>
                        <li><strong><span class="term explain" data-pronunciation="切克破因特" data-explain="Checkpoint：状态快照机制，类似游戏存档。定期保存任务状态，故障时从快照恢复">Checkpoint<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></strong>：定期保存状态快照，故障时可以恢复</li>
                        <li><strong><span class="term explain" data-pronunciation="赛无破因特" data-explain="Savepoint：手动触发的Checkpoint，用于升级和迁移。与Checkpoint区别：手动触发，需手动删除">Savepoint<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></strong>：手动触发的检查点，用于升级、迁移</li>
                        <li><strong><span class="term explain" data-pronunciation="沃特马克" data-explain="Watermark：处理乱序数据的时间戳机制。告诉Flink某个时间点之前的数据都已到达">Watermark（水位线）<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></strong>：处理乱序数据的时间戳机制</li>
                        <li><strong><span class="term explain" data-pronunciation="拜克普瑞射" data-explain="Backpressure：反压机制。下游处理慢时，上游自动降速，避免内存爆掉">反压（Backpressure）<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></strong>：下游处理慢了，上游自动降速</li>
                    </ul>
                </div>

                <div class="visualization-box">
                    <h4>🎯 Flink架构与Checkpoint流程</h4>
                    <div class="jvm-memory-demo">
                        <div class="tree-controls">
                            <button class="demo-btn" onclick="showFlinkArchitecture()">🏗️ Flink架构</button>
                            <button class="demo-btn" onclick="showCheckpointFlow()">💾 Checkpoint流程</button>
                            <button class="demo-btn" onclick="resetFlinkView()">🔄 重置</button>
                        </div>
                        
                        <div class="tree-canvas" id="flinkCanvas">
                            <svg width="100%" height="400" viewBox="0 0 800 400">
                                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Flink工作原理</text>
                                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
                            </svg>
                            <div class="tree-explanation">
                                <p id="flinkExplanation">点击按钮查看Flink架构或Checkpoint流程</p>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我在银行项目中用 Flink 做实时交易计算，每天处理 5000 万+ 笔交易流水。Flink 的优势是 Exactly-Once 语义保证数据不重不丢，而且有强大的状态管理能力，可以管理 TB 级状态数据。</p>
                    <p>我做过很多性能优化，比如 Kafka 分区扩容（从 64 扩到 256）、并行度调整（从 32 调到 128）、反压处理、Checkpoint 调优，最终把处理延迟从小时级降到秒级。"</p>
                </div>

                <div class="example-box">
                    <h4>性能优化案例（面试重点）</h4>
                    <ol>
                        <li><strong>Kafka 分区优化</strong>：核心 Topic 从 64 分区扩到 256 分区，Flink 并行度调到 128</li>
                        <li><strong>算子链优化</strong>：能合并的算子就合并，减少网络开销</li>
                        <li><strong>异步 IO</strong>：查外部数据库用异步方式，不阻塞流</li>
                        <li><strong>Checkpoint 优化</strong>：RocksDB 增量 Checkpoint，时间从 20 分钟降到 5 分钟</li>
                        <li><strong>资源调优</strong>：TaskManager 堆内存 8GB、堆外内存 4GB、RocksDB 缓存 2GB</li>
                    </ol>
                    <p><strong>优化效果：</strong>处理延迟从小时级降到秒级，Checkpoint 成功率从 70% 提升到 99%+</p>
                </div>
            </div>

            <button class="collapsible">Kafka - 分布式消息队列</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>分布式消息队列。生产者往 Kafka 里写数据，消费者从 Kafka 里读数据，中间有个缓冲。</p>
                    
                    <h4>好处</h4>
                    <p>解耦、削峰、异步</p>
                </div>

                <div class="visualization-box">
                    <h4>🎯 Kafka架构原理</h4>
                    <div class="jvm-memory-demo">
                        <div class="tree-controls">
                            <button class="demo-btn" onclick="showKafkaArchitecture()">📊 Kafka架构</button>
                            <button class="demo-btn" onclick="showKafkaPartition()">📦 分区机制</button>
                            <button class="demo-btn" onclick="resetKafkaView()">🔄 重置</button>
                        </div>
                        
                        <div class="tree-canvas" id="kafkaCanvas">
                            <svg width="100%" height="400" viewBox="0 0 800 400">
                                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Kafka工作原理</text>
                                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
                            </svg>
                            <div class="tree-explanation">
                                <p id="kafkaExplanation">点击按钮查看Kafka架构或分区机制</p>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们用 Kafka 做数据管道和异步任务队列。实时交易数据先写到 Kafka，然后多个下游（Flink、ES、数据湖）各自消费。</p>
                    <p>Kafka 的高吞吐量和持久化能力很强，我们做过分区优化，把核心 Topic 扩到 256 分区，配合 Flink 128 并行度，吞吐量提升很明显。"</p>
                </div>
            </div>

            <button class="collapsible">Hudi - 数据湖存储格式</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>数据湖存储格式，支持<span class="explain" data-explain="为什么叫流批一体？传统的：实时用Flink，离线用Hive，数据存两份。有了Hudi：Flink实时写入，Hive也能查，数据只存一份">流批一体<span class="explain-icon">❓</span></span>。传统数据仓库只能批量更新（每天跑一次），Hudi 可以实时增量更新。</p>
                    
                    <h4>两种表类型</h4>
                    <ul>
                        <li><strong><span class="term explain" data-pronunciation="靠批昂赖特（靠）" data-explain="COW怎么工作？每次更新都重写整个文件。优点：查询快（直接读）。缺点：写入慢。适合查询多、更新少的场景">Copy-On-Write（COW）<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></strong>：写时复制，查询快，适合实时增量数据</li>
                        <li><strong><span class="term explain" data-pronunciation="莫之昂瑞德（摸）" data-explain="MOR怎么工作？更新时只追加日志，查询时临时合并。优点：写入快。缺点：查询慢。适合更新多、查询少的场景（CDC）">Merge-On-Read（MOR）<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span></strong>：写时快，查询时合并，适合 <span class="term" data-pronunciation="C-D-C">CDC<span class="pronun-icon">🔊</span></span> 数据</li>
                    </ul>
                </div>

                <div class="visualization-box">
                    <h4>🎯 Hudi架构与表类型对比</h4>
                    <div class="jvm-memory-demo">
                        <div class="tree-controls">
                            <button class="demo-btn" onclick="showHudiArchitecture()">🏗️ Hudi架构</button>
                            <button class="demo-btn" onclick="showCOWvsMOR()">📊 COW vs MOR</button>
                            <button class="demo-btn" onclick="resetHudiView()">🔄 重置</button>
                        </div>
                        
                        <div class="tree-canvas" id="hudiCanvas">
                            <svg width="100%" height="400" viewBox="0 0 800 400">
                                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Hudi工作原理</text>
                                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
                            </svg>
                            <div class="tree-explanation">
                                <p id="hudiExplanation">点击按钮查看Hudi架构或表类型对比</p>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们用 Hudi 构建实时数据湖。<span class="explain" data-explain="为什么Flink+Kafka+Hudi组合？Kafka缓冲数据（削峰），Flink实时计算，Hudi持久化存储。三者配合实现'消息队列→实时计算→数据湖'的完整链路">Flink 实时消费 Kafka 数据写入 Hudi<span class="explain-icon">❓</span></span>，底层存在 HDFS。我们用 COW 表存实时增量数据，支持分钟级更新，Hive、Spark、Presto 都能直接查询。</p>
                    <p>数据分区策略是按<span class="explain" data-explain="为什么日期+Hash二级分区？只按日期：某天数据特别多，单个分区太大。只按Hash：按时间查询要扫所有分区。日期+Hash：既能按时间裁剪分区，又能避免数据倾斜">日期 + Hash 二级分区<span class="explain-icon">❓</span></span>，这样既能按时间查，又能避免数据倾斜。而且 Hudi 会自动同步元数据到 Hive Metastore，其他引擎就能直接查了。"</p>
                </div>
            </div>

            <button class="collapsible">Flink CDC - 数据库变更捕获</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>Flink 的 Change Data Capture 连接器，实时捕获数据库的变更（增删改），同步到下游。</p>
                    
                    <h4>支持的数据库</h4>
                    <p>Oracle（LogMiner）、DB2、MySQL（Binlog）</p>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我用 Flink CDC 做数据库实时同步，支持 Oracle、DB2、MySQL。它基于 Binlog（MySQL）或 LogMiner（Oracle）捕获增量数据，支持全量 + 增量模式。</p>
                    <p>而且能自动感知 Schema 变更，上游表加了字段，下游也会自动调整。基于 Checkpoint 机制，任务失败后能从断点恢复，保证数据不丢失、不重复。"</p>
                </div>
            </div>

            <button class="collapsible">K8s (Kubernetes) - 容器编排平台</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>是什么</h4>
                    <p>容器编排平台。Docker 只能管单个容器，K8s 管理一大堆容器。</p>
                    
                    <h4>核心功能</h4>
                    <p>自动调度、自动扩缩容、自动重启、负载均衡</p>
                </div>

                <div class="visualization-box">
                    <h4>🎯 K8s架构与核心组件</h4>
                    <div class="jvm-memory-demo">
                        <div class="tree-controls">
                            <button class="demo-btn" onclick="showK8sArchitecture()">🏗️ K8s架构</button>
                            <button class="demo-btn" onclick="showK8sComponents()">📦 核心组件</button>
                            <button class="demo-btn" onclick="resetK8sView()">🔄 重置</button>
                        </div>
                        
                        <div class="tree-canvas" id="k8sCanvas">
                            <svg width="100%" height="400" viewBox="0 0 800 400">
                                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">K8s工作原理</text>
                                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
                            </svg>
                            <div class="tree-explanation">
                                <p id="k8sExplanation">点击按钮查看K8s架构或核心组件</p>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们用 <span class="term explain" data-pronunciation="K8斯" data-explain="Kubernetes，容器编排平台，云原生基础设施。

✦ 核心功能：
• 自动调度：根据资源自动分配Pod
• 自愈：Pod挂了自动重启
• 弹性扩缩容：根据负载自动增减Pod
• 负载均衡：Service自动分发流量
• 滚动更新：零停机部署

✦ 核心概念：
• Pod：最小部署单元，包含1个或多个容器
• Service：Pod的访问入口，提供负载均衡
• Deployment：管理Pod副本数
• Namespace：资源隔离
• ConfigMap/Secret：配置管理

✦ 项目中的应用：
• Flink Native K8s：动态申请资源，用完释放
• AI服务部署：模型推理服务容器化
• 微服务编排：多个服务协同工作

✦ 面试要点：
• 为什么用K8s？→ 自动化运维、弹性扩缩容
• Pod和容器的区别？→ Pod是逻辑单元，可包含多个容器">K8s<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 部署 <span class="term explain" data-pronunciation="夫林克" data-explain="Apache Flink，实时流式计算引擎。

✦ 核心能力：
• 流批一体：同一套代码处理流数据和批数据
• 低延迟：毫秒级延迟，比Spark Streaming快
• 高吞吐：单机百万QPS
• Exactly-Once：数据不重不丢

✦ 核心概念：
• Checkpoint：状态快照，故障恢复
• Watermark：处理乱序数据的时间戳机制
• 状态管理：TB级状态数据（RocksDB）
• 反压：下游慢了自动降速

✦ vs Spark：
• Flink真流处理，Spark是微批
• Flink延迟更低（毫秒 vs 秒）
• Flink状态管理更强
• Spark生态更成熟

✦ 项目中的应用：
• 实时交易计算（5000万+笔/天）
• 实时数据入湖（Flink+Hudi）
• CDC数据同步（Oracle→数据湖）
• 实时风控（状态累计、规则触发）">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 作业和 <span class="term" data-pronunciation="诶爱">AI<span class="pronun-icon">🔊</span></span> 服务。<span class="term explain" data-pronunciation="夫林克" data-explain="Apache Flink，实时流式计算引擎。

✦ 核心能力：
• 流批一体：同一套代码处理流数据和批数据
• 低延迟：毫秒级延迟，比Spark Streaming快
• 高吞吐：单机百万QPS
• Exactly-Once：数据不重不丢

✦ 核心概念：
• Checkpoint：状态快照，故障恢复
• Watermark：处理乱序数据的时间戳机制
• 状态管理：TB级状态数据（RocksDB）
• 反压：下游慢了自动降速

✦ vs Spark：
• Flink真流处理，Spark是微批
• Flink延迟更低（毫秒 vs 秒）
• Flink状态管理更强
• Spark生态更成熟

✦ 项目中的应用：
• 实时交易计算（5000万+笔/天）
• 实时数据入湖（Flink+Hudi）
• CDC数据同步（Oracle→数据湖）
• 实时风控（状态累计、规则触发）">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> <span class="term" data-pronunciation="内踢舞">Native<span class="pronun-icon">🔊</span></span> <span class="term explain" data-pronunciation="K8斯" data-explain="Kubernetes，容器编排平台，云原生基础设施。

✦ 核心功能：
• 自动调度：根据资源自动分配Pod
• 自愈：Pod挂了自动重启
• 弹性扩缩容：根据负载自动增减Pod
• 负载均衡：Service自动分发流量
• 滚动更新：零停机部署

✦ 核心概念：
• Pod：最小部署单元，包含1个或多个容器
• Service：Pod的访问入口，提供负载均衡
• Deployment：管理Pod副本数
• Namespace：资源隔离
• ConfigMap/Secret：配置管理

✦ 项目中的应用：
• Flink Native K8s：动态申请资源，用完释放
• AI服务部署：模型推理服务容器化
• 微服务编排：多个服务协同工作

✦ 面试要点：
• 为什么用K8s？→ 自动化运维、弹性扩缩容
• Pod和容器的区别？→ Pod是逻辑单元，可包含多个容器">K8s<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 模式下，作业动态申请资源，跑完自动释放。而且 <span class="term explain" data-pronunciation="K8斯" data-explain="Kubernetes，容器编排平台，云原生基础设施。

✦ 核心功能：
• 自动调度：根据资源自动分配Pod
• 自愈：Pod挂了自动重启
• 弹性扩缩容：根据负载自动增减Pod
• 负载均衡：Service自动分发流量
• 滚动更新：零停机部署

✦ 核心概念：
• Pod：最小部署单元，包含1个或多个容器
• Service：Pod的访问入口，提供负载均衡
• Deployment：管理Pod副本数
• Namespace：资源隔离
• ConfigMap/Secret：配置管理

✦ 项目中的应用：
• Flink Native K8s：动态申请资源，用完释放
• AI服务部署：模型推理服务容器化
• 微服务编排：多个服务协同工作

✦ 面试要点：
• 为什么用K8s？→ 自动化运维、弹性扩缩容
• Pod和容器的区别？→ Pod是逻辑单元，可包含多个容器">K8s<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 有自愈能力，<span class="term" data-pronunciation="跑的">Pod<span class="pronun-icon">🔊</span></span> 挂了会自动重启。</p>
                    <p>我配置过 NameSpace 隔离、ResourceQuota 限额、PVC 持久化、Ingress 路由等。"</p>
                </div>
            </div>

            <button class="collapsible">⭐ 反压（Backpressure）- 流式计算核心概念</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>什么是反压</h4>
                    <p>数据处理链路中，下游处理速度慢于上游产生速度时，系统自动让上游降速，避免数据积压导致内存溢出。</p>
                </div>

                <div class="answer-box">
                    <h4>为什么会产生反压？</h4>
                    <p>举个例子：</p>
                    <ul>
                        <li><span class="term explain" data-pronunciation="咖夫卡" data-explain="消息队列，每秒产生1000条消息，数据来源">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 每秒产生 1000 条消息</li>
                        <li><span class="term explain" data-pronunciation="夫林克" data-explain="流式计算引擎，消费Kafka数据，每秒可以处理800条">Flink<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 每秒只能处理 800 条</li>
                        <li><span class="term explain" data-pronunciation="欧瑞口" data-explain="下游数据库，写入速度慢，每秒只能写100条，是反压的瓶颈">Oracle<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 每秒只能写 100 条</li>
                    </ul>
                    <p><strong>结果：</strong>数据在 Flink 里积压，内存不断增长，最终 OOM（内存溢出）崩溃。</p>
                </div>

                <div class="example-box">
                    <h4>Flink 如何处理反压？</h4>
                    <ol>
                        <li><strong>检测</strong>：Flink 监控算子的 <span class="term explain" data-pronunciation="巴佛" data-explain="OutputBufferUsage：输出缓冲区使用率。>80%说明下游处理不过来了">Buffer<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 使用率</li>
                        <li><strong>传导</strong>：下游满了 → 不再接收上游数据 → 上游 <span class="term" data-pronunciation="巴佛">Buffer<span class="pronun-icon">🔊</span></span> 也满了 → 逐级向上传导</li>
                        <li><strong>限速</strong>：最终传导到 <span class="term explain" data-pronunciation="搜死" data-explain="数据源头，比如Kafka Consumer。Source是Flink任务的起点">Source<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>，Source 自动降速（比如减少 <span class="term explain" data-pronunciation="咖夫卡" data-explain="Apache Kafka，分布式消息队列/流处理平台。

✦ 核心功能：
• 消息队列：生产者发消息，消费者订阅消息
• 流处理：实时处理数据流
• 高吞吐：百万级QPS，延迟毫秒级
• 持久化：消息存储在磁盘，不丢数据

✦ 核心概念：
• Topic：消息主题，类似数据库的表
• Partition：分区，提升并行度
• Offset：消息位置，消费进度
• Consumer Group：消费者组，实现负载均衡

✦ 为什么用它：
• 解耦：生产者和消费者独立
• 削峰：应对流量突增
• 异步：提升系统响应速度
• 可靠：消息不丢失、不重复

✦ 项目中的应用：
• 异步任务队列（AI问答）
• 实时数据采集（交易流水）
• 日志收集
• 事件驱动架构">Kafka<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 的 fetch 频率）</li>
                        <li><strong>恢复</strong>：下游处理完积压数据 → Buffer 降低 → 上游逐步恢复正常速度</li>
                    </ol>
                </div>

                <div class="warning-box">
                    <h4>如何解决反压问题？</h4>
                    <ol>
                        <li><strong><span class="explain" data-explain="为什么扩并行度？并行度=同时处理的任务数。从32扩到128，处理能力提升4倍">增加并行度<span class="explain-icon">❓</span></span></strong>：从 32 调到 128，处理能力提升</li>
                        <li><strong><span class="explain" data-explain="为什么异步IO？同步写Oracle：处理一条→等Oracle写完→处理下一条（慢）。异步IO：处理一条→发给Oracle→不等待→继续处理下一条（快）">异步 IO<span class="explain-icon">❓</span></span></strong>：写数据库用异步方式，不阻塞主流程</li>
                        <li><strong><span class="explain" data-explain="为什么算子链？独立算子：数据要序列化→网络传输（慢）。算子链：合并算子→内存直接传递（快）">算子链优化<span class="explain-icon">❓</span></span></strong>：合并算子，减少数据传输</li>
                        <li><strong><span class="explain" data-explain="为什么优化下游？比如Oracle批量写入（一次写100条）比单条写入快10倍">优化下游性能<span class="explain-icon">❓</span></span></strong>：数据库批量写入、加索引、分库分表</li>
                    </ol>
                </div>

                <div class="example-box">
                    <h4>面试这么说</h4>
                    <p>"在银行项目中遇到过严重的反压问题。Kafka 每秒来几千条，但 Oracle 写入慢，导致数据积压。</p>
                    <p>我们的解决方案：一是 Flink 并行度从 32 调到 128，二是写 Oracle 改成异步批量写入（攒够 100 条或 5 秒超时统一写），三是算子链优化减少数据传输。</p>
                    <p>优化后反压基本消除，处理延迟从几十分钟降到秒级。"</p>
                </div>
            </div>

            <button class="collapsible">其他大数据技术</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>HDFS - 分布式文件系统</h4>
                    <p>存海量数据的分布式存储，数据分块存储在多台机器上，有副本保证可靠性（3 副本）。</p>

                    <h4>Hive - 数据仓库</h4>
                    <p>基于 Hadoop 的数据仓库，用 SQL 查询 HDFS 数据。底层会转成 MapReduce 或 Spark 作业去执行。</p>

                    <h4>Spark - 分布式计算引擎</h4>
                    <p>MapReduce 的升级版，基于内存计算，速度快 10-100 倍。可以用 Spark SQL 写 SQL，也可以用 Spark Streaming 做准实时计算。</p>

                    <h4>HBase - 分布式 NoSQL 数据库</h4>
                    <p>存大量结构化数据，支持随机读写，毫秒级查询。适合存时序数据、日志、历史明细等。</p>
                    <p><strong><span class="explain" data-explain="为什么地域+时间戳反转？只用时间戳：数据都写到一个Region（热点）。时间戳反转：把最新数据分散到不同Region（避免热点）。加地域前缀：按地域查询只扫对应Region（快）">RowKey 优化<span class="explain-icon">❓</span></span>：</strong>用地域 + 时间戳反转的复合 RowKey，避免热点，查询性能提升 3 倍。</p>

                    <h4>RocksDB - 嵌入式 KV 存储</h4>
                    <p>Flink 用来存状态数据的。支持增量 Checkpoint，适合大状态场景。</p>
                </div>
            </div>

            <hr>

            <h3>Java 技术栈</h3>

            <div class="tech-box">
                <h4>Spring Boot</h4>
                <p>Java 微服务开发框架，开箱即用，自动配置。</p>

                <h4>Dubbo</h4>
                <p>阿里开源的微服务 RPC 框架，提供高性能的远程调用能力。</p>

                <h4>MyBatis-Plus / MyBatis-Flex</h4>
                <p>数据库 ORM 框架，不用写 SQL（或者少写 SQL），通过 Java 对象操作数据库。</p>

                <h4>Sentinel</h4>
                <p>阿里开源的限流降级框架，防止系统被打垮。</p>
            </div>

            <hr>

            <h3>数据库技术</h3>

            <div class="tech-box">
                <h4>MySQL / PostgreSQL</h4>
                <p>关系型数据库。MySQL 简单稳定，PostgreSQL 功能更强大。</p>

                <h4>ClickHouse</h4>
                <p>列式存储 OLAP 数据库，查询特别快。特别适合大宽表、海量数据的统计分析。</p>

                <h4>Redis</h4>
                <p>内存 KV 数据库，缓存必备。用来做缓存、分布式锁、Session 存储。</p>

                <h4>Elasticsearch（ES）</h4>
                <p>搜索引擎，也能当 NoSQL 数据库。用来做日志存储和全文搜索。</p>

                <h4>Milvus</h4>
                <p>专门的向量数据库，查询性能比 ES 好。用 HNSW 索引算法，支持百万级文档毫秒级检索。</p>

                <h4>达梦数据库（DM）</h4>
                <p>国产商业数据库。我做过 Hive 和 Ranger 的达梦适配，包括 DDL 语法、数据类型映射、字符集兼容等。</p>
            </div>

            <hr>

            <h3>监控运维</h3>

            <div class="tech-box">
                <h4>Prometheus + Grafana</h4>
                <p><strong>Prometheus</strong>：采集指标数据（CPU、内存、QPS、延迟等）</p>
                <p><strong>Grafana</strong>：可视化展示，做漂亮的监控大屏</p>

                <h4>Ranger</h4>
                <p>Hadoop 生态的权限管理系统，可以控制谁能访问哪些 HDFS 路径、哪些 Hive 表。</p>

                <h4>Kerberos</h4>
                <p>身份认证协议，Hadoop 集群的安全认证机制。</p>
            </div>
        </div>

        <!-- Java后端技术（独立tab） -->
        <div class="content" id="content3">
            <h2>☕ Java后端技术详解</h2>
            
            <p class="warning-box">
                <strong>💡 重要提示：</strong>Java后端是很多公司的必考点，尤其是Spring Boot、集合框架、并发、JVM。这里整理了高频面试题和标准答案。
            </p>
            
            <button class="collapsible">⭐ Spring Boot - 核心原理与面试要点</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>自动配置原理（高频考点）</h4>
                    <p><strong>核心流程：</strong></p>
                    <ol>
                        <li>@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan</li>
                        <li>@EnableAutoConfiguration → 导入AutoConfigurationImportSelector</li>
                        <li>读取META-INF/spring.factories中的自动配置类</li>
                        <li>根据@Conditional条件判断是否生效</li>
                        <li>满足条件则创建Bean并注册到Spring容器</li>
                    </ol>
                    
                    <h4>启动流程（7个关键步骤）</h4>
                    <pre>1. 创建SpringApplication对象
2. 运行run方法
3. 准备Environment（环境配置）
4. 打印Banner
5. 创建ApplicationContext（容器）
6. 准备Context（自动配置生效）
7. 刷新Context（启动内嵌Tomcat）</pre>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"Spring Boot的核心是自动配置。启动时会扫描classpath下的jar包，根据@Conditional注解判断条件，自动创建需要的Bean。</p>
                    <p>比如引入了spring-boot-starter-web，就会自动配置Tomcat、DispatcherServlet等。引入了spring-boot-starter-data-jpa，就会自动配置DataSource、EntityManager等。</p>
                    <p>这样就实现了约定优于配置，开发者只需关注业务代码，不用写大量XML配置。"</p>
                </div>

                <div class="example-box">
                    <h4>常用注解</h4>
                    <ul>
                        <li><strong>@RestController</strong> = @Controller + @ResponseBody，返回JSON</li>
                        <li><strong>@Autowired</strong>：自动注入Bean（byType）</li>
                        <li><strong>@Service/@Repository</strong>：标记业务层/数据层组件</li>
                        <li><strong>@Transactional</strong>：声明式事务</li>
                        <li><strong>@ControllerAdvice</strong>：全局异常处理</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">⭐ Java集合框架 - HashMap原理（必考）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>HashMap底层结构</h4>
                    <p><strong>JDK1.8：数组 + 链表 + 红黑树</strong></p>
                    <pre>初始容量16，加载因子0.75
当size > capacity * 0.75时扩容2倍

数组每个位置叫bucket（桶）
hash冲突时，同一个bucket用链表连接
链表长度≥8且数组长度≥64时，链表转红黑树
红黑树节点≤6时，退化为链表</pre>
                    
                    <h4>put方法流程</h4>
                    <ol>
                        <li>计算key的hash值：(h = key.hashCode()) ^ (h >>> 16)</li>
                        <li>确定数组下标：hash & (n-1)</li>
                        <li>如果该位置为null，直接插入</li>
                        <li>如果该位置有值，判断key是否相同</li>
                        <li>相同则替换value，不同则追加到链表/红黑树</li>
                        <li>插入后size++，判断是否需要扩容</li>
                    </ol>

                    <h4>为什么用红黑树？</h4>
                    <p>链表查询O(n)，红黑树查询O(log n)。当冲突严重时，红黑树性能更好。</p>
                    
                    <div class="visualization-box">
                        <h4>🎯 红黑树交互式演示</h4>
                        <div class="red-black-tree-demo">
                            <div class="tree-controls">
                                <button class="demo-btn" onclick="startRotateLeft()">👈 左旋演示</button>
                                <button class="demo-btn" onclick="startRotateRight()">👉 右旋演示</button>
                                <button class="demo-btn" onclick="startRecolor()">🎨 变色演示</button>
                                <button class="demo-btn" onclick="resetTree()">🔄 重置</button>
                            </div>
                            
                            <!-- 动画控制面板 -->
                            <div class="tree-animation-controls" id="animationControls" style="display: none;">
                                <div class="animation-btn-group">
                                    <button class="anim-btn" id="prevStepBtn" onclick="prevStep()">◀ 上一步</button>
                                    <button class="anim-btn" id="playPauseBtn" onclick="togglePlayPause()">▶ 播放</button>
                                    <button class="anim-btn" id="nextStepBtn" onclick="nextStep()">下一步 ▶</button>
                                </div>
                                
                                <div class="step-indicator">
                                    <span id="stepIndicator">步骤 0/5</span>
                                </div>
                                
                                <div class="speed-control">
                                    <span class="speed-label">速度:</span>
                                    <button class="speed-btn" onclick="setSpeed(0.5)">慢速</button>
                                    <button class="speed-btn active" onclick="setSpeed(1)">正常</button>
                                    <button class="speed-btn" onclick="setSpeed(2)">快速</button>
                                </div>
                            </div>
                            <div class="tree-canvas" id="treeCanvas">
                                <svg width="100%" height="450" viewBox="0 0 900 450">
                                    <!-- 初始状态：完整的红黑树 (9个节点) -->
                                    <g id="tree-initial">
                                        <!-- 第1层：根节点 (黑) -->
                                        <circle cx="450" cy="40" r="28" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                                        <text x="450" y="48" text-anchor="middle" fill="white" font-size="18" font-weight="bold">30</text>
                                        
                                        <!-- 第2层 -->
                                        <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="280" cy="120" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                                        <text x="280" y="128" text-anchor="middle" fill="white" font-size="17" font-weight="bold">15</text>
                                        
                                        <line x1="450" y1="68" x2="620" y2="110" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="620" cy="120" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                                        <text x="620" y="128" text-anchor="middle" fill="white" font-size="17" font-weight="bold">50</text>
                                        
                                        <!-- 第3层 -->
                                        <line x1="280" y1="146" x2="180" y2="190" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="180" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                                        <text x="180" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">10</text>
                                        
                                        <line x1="280" y1="146" x2="380" y2="190" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="380" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                                        <text x="380" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">25</text>
                                        
                                        <line x1="620" y1="146" x2="520" y2="190" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="520" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                                        <text x="520" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">40</text>
                                        
                                        <line x1="620" y1="146" x2="720" y2="190" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="720" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                                        <text x="720" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">60</text>
                                        
                                        <!-- 第4层 -->
                                        <line x1="180" y1="224" x2="120" y2="270" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="120" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                                        <text x="120" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">5</text>
                                        
                                        <line x1="180" y1="224" x2="240" y2="270" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="240" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                                        <text x="240" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">12</text>
                                        
                                        <line x1="720" y1="224" x2="680" y2="270" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="680" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                                        <text x="680" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">55</text>
                                        
                                        <line x1="720" y1="224" x2="760" y2="270" stroke="#95a5a6" stroke-width="2"/>
                                        <circle cx="760" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                                        <text x="760" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">70</text>
                                        
                                        <!-- 树高度标注 -->
                                        <text x="50" y="150" fill="#666" font-size="13">树高: 4</text>
                                        <text x="50" y="170" fill="#666" font-size="13">黑高: 3</text>
                                    </g>
                                </svg>
                                <div class="tree-explanation">
                                    <p><span style="color: #2c3e50;">⚫</span> 黑色节点 &nbsp;&nbsp; <span style="color: #e74c3c;">🔴</span> 红色节点</p>
                                    <p id="treeExplanation">当前是平衡的红黑树，点击上方按钮查看插入新节点后的修复过程</p>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="tech-box" style="background: #fff3cd; border-left: 4px solid #ffc107;">
                        <h4>🎓 红黑树5大规则（重要！）</h4>
                        <ol>
                            <li><strong>根节点必须是黑色</strong></li>
                            <li><strong>红色节点的子节点必须是黑色</strong>（不能有连续的红色节点）</li>
                            <li><strong>叶子节点（NULL）都是黑色</strong></li>
                            <li><strong>从根到叶子的所有路径，黑色节点数量相同</strong>（保证平衡）</li>
                            <li><strong>新插入的节点是红色</strong></li>
                        </ol>
                        
                        <h4>⚙️ 如何保持平衡</h4>
                        <p><strong>三种操作：</strong></p>
                        <ul>
                            <li><strong>变色</strong>：切换节点的红/黑颜色</li>
                            <li><strong>左旋</strong>：逆时针旋转子树</li>
                            <li><strong>右旋</strong>：顺时针旋转子树</li>
                        </ul>
                    </div>

                    <h4>为什么容量是2的幂次？</h4>
                    <p>为了让hash & (n-1)均匀分布。如果n=16，n-1=15（二进制1111），hash与15按位与，相当于取hash的低4位，分布均匀。</p>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"HashMap底层是数组+链表+红黑树。put时先计算hash值，然后对数组长度取模得到下标。如果发生hash冲突，会用链表连接。当链表长度超过8时，转为红黑树提升查询效率。</p>
                    <p>容量必须是2的幂次，这样hash & (n-1)才能均匀分布。加载因子0.75是时间和空间的权衡，太大容易冲突，太小浪费空间。"</p>
                </div>

                <div class="warning-box">
                    <h4>线程安全问题</h4>
                    <p><strong>HashMap线程不安全！</strong></p>
                    <ul>
                        <li>多线程put会导致数据丢失</li>
                        <li>JDK1.7扩容会导致死循环（1.8已修复）</li>
                        <li>解决方案：ConcurrentHashMap（分段锁→CAS+synchronized）</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">ArrayList vs LinkedList - 性能对比</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>对比表格</h4>
                    <table>
                        <tr>
                            <th>操作</th>
                            <th>ArrayList</th>
                            <th>LinkedList</th>
                        </tr>
                        <tr>
                            <td>底层结构</td>
                            <td>数组</td>
                            <td>双向链表</td>
                        </tr>
                        <tr>
                            <td>随机访问get(i)</td>
                            <td>O(1) ⭐快</td>
                            <td>O(n) 慢</td>
                        </tr>
                        <tr>
                            <td>头部插入</td>
                            <td>O(n) 慢（要移动元素）</td>
                            <td>O(1) ⭐快</td>
                        </tr>
                        <tr>
                            <td>尾部插入</td>
                            <td>O(1) ⭐快（扩容时O(n)）</td>
                            <td>O(1) 快</td>
                        </tr>
                        <tr>
                            <td>中间插入</td>
                            <td>O(n) 慢</td>
                            <td>O(n) 慢（需先找到位置）</td>
                        </tr>
                        <tr>
                            <td>内存占用</td>
                            <td>连续内存，扩容浪费</td>
                            <td>非连续，Node额外空间</td>
                        </tr>
                    </table>
                </div>

                <div class="answer-box">
                    <h4>如何选择</h4>
                    <ul>
                        <li>⭐ <strong>90%场景用ArrayList</strong>：随机访问多、顺序插入</li>
                        <li>只有频繁头部插入/删除才用LinkedList</li>
                        <li>队列场景：ArrayDeque性能比LinkedList好</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">⭐ Java并发 - synchronized vs Lock</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>synchronized（JVM实现）</h4>
                    <p><strong>使用方式：</strong></p>
                    <ul>
                        <li>synchronized方法：锁住this对象</li>
                        <li>synchronized静态方法：锁住Class对象</li>
                        <li>synchronized代码块：锁住指定对象</li>
                    </ul>
                    
                    <p><strong>锁升级机制（JDK1.6优化）：</strong></p>
                    <pre>无锁 → 偏向锁 → 轻量级锁 → 重量级锁

• 偏向锁：第一个线程获取锁，记录线程ID，后续无需CAS
• 轻量级锁：多线程竞争不激烈，CAS自旋
• 重量级锁：竞争激烈，阻塞等待（性能差）</pre>

                    <h4>Lock（java.util.concurrent）</h4>
                    <p><strong>核心接口：</strong></p>
                    <ul>
                        <li>ReentrantLock：可重入锁，支持公平/非公平</li>
                        <li>ReadWriteLock：读写锁，读不互斥，写互斥</li>
                    </ul>
                    
                    <p><strong>优势：</strong></p>
                    <ul>
                        <li>可中断：lockInterruptibly()</li>
                        <li>可超时：tryLock(timeout)</li>
                        <li>可公平：new ReentrantLock(true)</li>
                        <li>条件变量：Condition支持多个等待队列</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>如何选择</h4>
                    <ul>
                        <li>⭐ <strong>默认用synchronized</strong>：简单、JVM优化好</li>
                        <li>需要高级功能才用Lock（超时、中断、公平锁）</li>
                        <li>读多写少：ReadWriteLock</li>
                    </ul>
                </div>

                <div class="warning-box">
                    <h4>常见面试题</h4>
                    <p><strong>Q：volatile的作用？</strong></p>
                    <p>A：保证可见性和有序性，但不保证原子性。适用于标志位场景。</p>
                    
                    <p><strong>Q：ThreadLocal原理？</strong></p>
                    <p>A：每个Thread有个ThreadLocalMap，key是ThreadLocal对象，value是线程私有数据。避免了锁竞争。</p>
                </div>
            </div>

            <button class="collapsible">⭐ 线程池ThreadPoolExecutor（高频）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>为什么要用线程池？</h4>
                    <ul>
                        <li>✅ <strong>降低资源消耗</strong>：复用线程，避免频繁创建/销毁</li>
                        <li>✅ <strong>提高响应速度</strong>：任务到达时无需等待线程创建</li>
                        <li>✅ <strong>提高可管理性</strong>：统一分配、监控、调优</li>
                    </ul>
                    
                    <h4>七大参数</h4>
                    <pre><span class="term explain" data-pronunciation="kɔː sɪz" data-explain="核心线程数：始终存活的线程数量，即使空闲也不会销毁">corePoolSize</span> - 核心线程数
<span class="term explain" data-pronunciation="mæks ɪməm puːl sɪz" data-explain="最大线程数：线程池允许的最大线程数量">maximumPoolSize</span> - 最大线程数
<span class="term explain" data-pronunciation="kiːp əˈlaɪv taɪm" data-explain="非核心线程空闲存活时间：超过这个时间会被销毁">keepAliveTime</span> - 非核心线程空闲存活时间
unit - 时间单位
<span class="term explain" data-pronunciation="wɜːk kjuː" data-explain="任务队列：核心线程满后，新任务进入队列等待">workQueue</span> - 任务队列（阻塞队列）
<span class="term explain" data-pronunciation="θred ˈfæktəri" data-explain="线程工厂：用于创建新线程，可自定义线程名称">threadFactory</span> - 线程工厂
<span class="term explain" data-pronunciation="hændlə" data-explain="拒绝策略：队列满且线程达到最大时的处理方式">handler</span> - 拒绝策略</pre>
                    
                    <h4>执行流程</h4>
                    <ol>
                        <li>线程数 < corePoolSize：<strong>创建核心线程</strong>执行任务</li>
                        <li>线程数 = corePoolSize：任务<strong>进入队列</strong>等待</li>
                        <li>队列满 && 线程数 < maximumPoolSize：<strong>创建非核心线程</strong>执行</li>
                        <li>队列满 && 线程数 = maximumPoolSize：<strong>执行拒绝策略</strong></li>
                    </ol>
                    
                    <div class="visualization-box">
                        <h4>🎯 线程池工作流程可视化</h4>
                        <div class="jvm-memory-demo">
                            <div class="tree-controls">
                                <button class="demo-btn" onclick="showThreadPoolFlow()">🔄 查看执行流程</button>
                                <button class="demo-btn" onclick="showRejectionPolicy()">🚫 拒绝策略对比</button>
                                <button class="demo-btn" onclick="resetThreadPoolView()">🔄 重置</button>
                            </div>
                            
                            <div class="tree-canvas" id="threadPoolCanvas">
                                <svg width="100%" height="400" viewBox="0 0 800 400">
                                    <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">线程池工作原理</text>
                                    <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细流程</text>
                                </svg>
                                <div class="tree-explanation">
                                    <p id="threadPoolExplanation">点击按钮查看线程池工作流程或拒绝策略</p>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="tech-box">
                    <h4>四种拒绝策略</h4>
                    <ul>
                        <li><strong class="term explain" data-pronunciation="əˈbɔːt ˈpɒləsi" data-explain="中止策略：默认策略，抛出RejectedExecutionException异常">AbortPolicy</strong>（默认）：抛异常 <code>RejectedExecutionException</code></li>
                        <li><strong class="term explain" data-pronunciation="ˈkɔːlə rʌnz ˈpɒləsi" data-explain="调用者运行策略：由提交任务的线程自己执行该任务">CallerRunsPolicy</strong>：调用者线程执行（降级策略）</li>
                        <li><strong class="term explain" data-pronunciation="dɪˈskɑːd ˈpɒləsi" data-explain="丢弃策略：直接丢弃新任务，不抛异常">DiscardPolicy</strong>：直接丢弃，不抛异常</li>
                        <li><strong class="term explain" data-pronunciation="dɪˈskɑːd ˈəʊldɪst ˈpɒləsi" data-explain="丢弃最旧策略：丢弃队列中最早的任务，把新任务加入队列">DiscardOldestPolicy</strong>：丢弃队列最老的任务</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>常见线程池类型</h4>
                    <p><strong class="term explain" data-pronunciation="fɪkst θred puːl" data-explain="固定大小线程池：核心线程数=最大线程数，使用无界队列">FixedThreadPool</strong>：固定大小，适合负载均衡的服务器</p>
                    <pre>corePoolSize = maxPoolSize = n
队列：LinkedBlockingQueue（无界）</pre>
                    
                    <p><strong class="term explain" data-pronunciation="ˈsɪŋɡl θred ɪɡˈzekjətə" data-explain="单线程执行器：只有1个线程，保证任务顺序执行">SingleThreadExecutor</strong>：单线程，任务顺序执行</p>
                    <pre>corePoolSize = maxPoolSize = 1</pre>
                    
                    <p><strong class="term explain" data-pronunciation="ˈkæʃt θred puːl" data-explain="缓存线程池：核心线程数为0，最大无限制，适合大量短任务">CachedThreadPool</strong>：无核心线程，来任务就创建</p>
                    <pre>corePoolSize = 0, maxPoolSize = Integer.MAX
队列：SynchronousQueue（直接交付）</pre>
                    
                    <p><strong class="term explain" data-pronunciation="ˈʃedjuːld θred puːl" data-explain="定时任务线程池：支持定时和周期性任务执行">ScheduledThreadPool</strong>：定时任务</p>
                </div>

                <div class="warning-box">
                    <h4>⚠️ 面试重点</h4>
                    <p><strong>Q：为什么不推荐用Executors创建线程池？</strong></p>
                    <p>A：因为容易OOM（内存溢出）：</p>
                    <ul>
                        <li>FixedThreadPool/SingleThreadExecutor：队列无界（Integer.MAX_VALUE），堆积大量任务</li>
                        <li>CachedThreadPool：线程数无限制，创建大量线程耗尽内存</li>
                    </ul>
                    <p><strong>推荐：</strong>手动new ThreadPoolExecutor，明确指定参数</p>
                    
                    <p><strong>Q：如何合理设置线程池大小？</strong></p>
                    <ul>
                        <li><strong>CPU密集型</strong>：线程数 = CPU核心数 + 1</li>
                        <li><strong>IO密集型</strong>：线程数 = CPU核心数 × (1 + IO耗时/CPU耗时)</li>
                        <li><strong>经验公式</strong>：2 × CPU核心数</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">⭐ JVM内存模型与GC（高频）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>JVM内存结构</h4>
                    <pre>堆（Heap）- 存放对象实例，GC主战场
    新生代（Young）- Eden + Survivor0 + Survivor1
    老年代（Old）- 长期存活对象
    
方法区（MetaSpace）- 类信息、常量池

栈（Stack）- 每个线程私有，存储局部变量、方法调用
    栈帧 = 局部变量表 + 操作数栈 + 动态链接 + 返回地址
    
程序计数器 - 记录当前执行指令地址

本地方法栈 - Native方法</pre>

                    <div class="visualization-box">
                        <h4>🎯 JVM内存结构可视化</h4>
                        <div class="jvm-memory-demo">
                            <div class="tree-controls">
                                <button class="demo-btn" onclick="showJVMMemory()">📊 查看内存布局</button>
                                <button class="demo-btn" onclick="showObjectCreation()">🎨 对象创建过程</button>
                                <button class="demo-btn" onclick="resetJVMView()">🔄 重置</button>
                            </div>
                            
                            <div class="tree-canvas" id="jvmCanvas">
                                <svg width="100%" height="400" viewBox="0 0 700 400">
                                    <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">JVM运行时数据区</text>
                                    
                                    <!-- 堆（Heap） -->
                                    <rect x="50" y="60" width="280" height="280" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                                    <text x="190" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">堆（Heap）</text>
                                    <text x="190" y="105" text-anchor="middle" font-size="12" fill="#666">线程共享，GC主战场</text>
                                    
                                    <!-- 新生代 -->
                                    <rect x="70" y="120" width="240" height="100" fill="#c8e6c9" stroke="#66bb6a" stroke-width="2" rx="5"/>
                                    <text x="190" y="140" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">新生代（Young）</text>
                                    <rect x="80" y="150" width="70" height="60" fill="#81c784" stroke="#388e3c" stroke-width="1"/>
                                    <text x="115" y="170" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">Eden</text>
                                    <text x="115" y="185" text-anchor="middle" font-size="10" fill="#fff">80%</text>
                                    <rect x="160" y="150" width="65" height="60" fill="#a5d6a7" stroke="#388e3c" stroke-width="1"/>
                                    <text x="192" y="170" text-anchor="middle" font-size="11" fill="#2c3e50">S0</text>
                                    <text x="192" y="185" text-anchor="middle" font-size="10" fill="#2c3e50">10%</text>
                                    <rect x="235" y="150" width="65" height="60" fill="#a5d6a7" stroke="#388e3c" stroke-width="1"/>
                                    <text x="267" y="170" text-anchor="middle" font-size="11" fill="#2c3e50">S1</text>
                                    <text x="267" y="185" text-anchor="middle" font-size="10" fill="#2c3e50">10%</text>
                                    
                                    <!-- 老年代 -->
                                    <rect x="70" y="235" width="240" height="90" fill="#fff9c4" stroke="#fbc02d" stroke-width="2" rx="5"/>
                                    <text x="190" y="260" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">老年代（Old）</text>
                                    <text x="190" y="280" text-anchor="middle" font-size="11" fill="#666">长期存活对象</text>
                                    <text x="190" y="295" text-anchor="middle" font-size="11" fill="#666">经历15次GC</text>
                                    
                                    <!-- 方法区 -->
                                    <rect x="360" y="60" width="280" height="120" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="8"/>
                                    <text x="500" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">方法区（MetaSpace）</text>
                                    <text x="500" y="105" text-anchor="middle" font-size="12" fill="#666">类信息、常量、静态变量</text>
                                    <rect x="380" y="115" width="230" height="50" fill="#b3e5fc" stroke="#0288d1" stroke-width="1"/>
                                    <text x="495" y="135" text-anchor="middle" font-size="11" fill="#2c3e50">类元数据</text>
                                    <text x="495" y="150" text-anchor="middle" font-size="10" fill="#666">Class信息、字段、方法</text>
                                    
                                    <!-- 栈 -->
                                    <rect x="360" y="200" width="130" height="140" fill="#fce4ec" stroke="#e91e63" stroke-width="3" rx="8"/>
                                    <text x="425" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">栈（Stack）</text>
                                    <text x="425" y="240" text-anchor="middle" font-size="11" fill="#666">线程私有</text>
                                    <rect x="375" y="250" width="100" height="25" fill="#f8bbd0" stroke="#c2185b" stroke-width="1"/>
                                    <text x="425" y="267" text-anchor="middle" font-size="10" fill="#2c3e50">栈帧3</text>
                                    <rect x="375" y="280" width="100" height="25" fill="#f8bbd0" stroke="#c2185b" stroke-width="1"/>
                                    <text x="425" y="297" text-anchor="middle" font-size="10" fill="#2c3e50">栈帧2</text>
                                    <rect x="375" y="310" width="100" height="25" fill="#f48fb1" stroke="#c2185b" stroke-width="1"/>
                                    <text x="425" y="327" text-anchor="middle" font-size="10" fill="#fff" font-weight="bold">栈帧1</text>
                                    
                                    <!-- 程序计数器 -->
                                    <rect x="510" y="200" width="130" height="65" fill="#f3e5f5" stroke="#9c27b0" stroke-width="3" rx="8"/>
                                    <text x="575" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">程序计数器</text>
                                    <text x="575" y="245" text-anchor="middle" font-size="11" fill="#666">当前指令地址</text>
                                    
                                    <!-- 本地方法栈 -->
                                    <rect x="510" y="275" width="130" height="65" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                                    <text x="575" y="300" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">本地方法栈</text>
                                    <text x="575" y="320" text-anchor="middle" font-size="11" fill="#666">Native方法</text>
                                    
                                    <!-- 说明 -->
                                    <text x="350" y="370" text-anchor="middle" font-size="12" fill="#666">线程共享：堆、方法区 | 线程私有：栈、程序计数器、本地方法栈</text>
                                </svg>
                                <div class="tree-explanation">
                                    <p id="jvmExplanation">点击按钮查看内存布局或对象创建过程</p>
                                </div>
                            </div>
                        </div>
                    </div>

                    <h4>GC算法</h4>
                    <ul>
                        <li><strong>标记-清除</strong>：标记存活对象，清除未标记对象。缺点：内存碎片</li>
                        <li><strong>复制算法</strong>：新生代用。分两块，存活对象复制到另一块。优点：无碎片。缺点：浪费空间</li>
                        <li><strong>标记-整理</strong>：老年代用。标记后，存活对象移到一端，清除边界外的对象</li>
                    </ul>

                    <div class="visualization-box">
                        <h4>🎯 GC算法工作过程演示</h4>
                        <div class="jvm-memory-demo">
                            <div class="tree-controls">
                                <button class="demo-btn" onclick="showMarkSweep()">🔍 标记-清除</button>
                                <button class="demo-btn" onclick="showCopyGC()">📋 复制算法</button>
                                <button class="demo-btn" onclick="showMarkCompact()">🎯 标记-整理</button>
                                <button class="demo-btn" onclick="resetGCView()">🔄 重置</button>
                            </div>
                            
                            <div class="tree-canvas" id="gcCanvas">
                                <svg width="100%" height="300" viewBox="0 0 700 300">
                                    <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">GC算法对比</text>
                                    <text x="350" y="150" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看不同GC算法的工作过程</text>
                                </svg>
                                <div class="tree-explanation">
                                    <p id="gcExplanation">选择一个GC算法查看详细演示</p>
                                </div>
                            </div>
                        </div>
                    </div>

                    <h4>常见GC器</h4>
                    <table>
                        <tr>
                            <th>GC器</th>
                            <th>特点</th>
                            <th>适用场景</th>
                        </tr>
                        <tr>
                            <td>Serial</td>
                            <td>单线程，STW长</td>
                            <td>小应用</td>
                        </tr>
                        <tr>
                            <td>Parallel</td>
                            <td>多线程，吞吐量高</td>
                            <td>后台任务</td>
                        </tr>
                        <tr>
                            <td>CMS</td>
                            <td>并发标记，STW短</td>
                            <td>低延迟应用</td>
                        </tr>
                        <tr>
                            <td>G1⭐</td>
                            <td>分区回收，可预测停顿</td>
                            <td>大堆内存（推荐）</td>
                        </tr>
                        <tr>
                            <td>ZGC</td>
                            <td>STW<10ms，TB级堆</td>
                            <td>超大内存、极低延迟</td>
                        </tr>
                    </table>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"JVM内存主要分堆、栈、方法区。堆是GC的主战场，分新生代和老年代。新生代采用复制算法，老年代用标记-整理。</p>
                    <p>我们项目用的是G1 GC，它把堆分成多个region，优先回收垃圾最多的region，可以设置期望停顿时间。相比CMS，G1不会产生内存碎片，适合大堆内存场景。"</p>
                </div>

                <div class="warning-box">
                    <h4>GC调优要点</h4>
                    <ul>
                        <li>设置合理的堆大小：-Xms和-Xmx设置一样大，避免扩容</li>
                        <li>新生代别太小：避免频繁Minor GC</li>
                        <li>老年代别太小：避免Full GC</li>
                        <li>监控GC日志：分析停顿时间和频率</li>
                    </ul>
                </div>
            </div>
        </div>

        <!-- 前端基础知识（新增tab） -->
        <div class="content" id="content4">
            <h2>🎨 前端基础知识</h2>
            
            <p class="warning-box">
                <strong>💡 定位说明：</strong>前端占比较低，掌握基础知识能应付全栈岗位面试即可。重点是Vue/React基本概念和常见面试题。
            </p>

            <button class="collapsible">⭐ Vue.js - 核心概念（高频）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>Vue2 vs Vue3 核心区别</h4>
                    <table>
                        <tr>
                            <th>特性</th>
                            <th>Vue 2</th>
                            <th>Vue 3</th>
                        </tr>
                        <tr>
                            <td><strong>响应式系统</strong></td>
                            <td>Object.defineProperty</td>
                            <td>Proxy（性能更好）</td>
                        </tr>
                        <tr>
                            <td><strong>组合式API</strong></td>
                            <td>Options API（data/methods）</td>
                            <td>Composition API（setup）</td>
                        </tr>
                        <tr>
                            <td><strong>性能</strong></td>
                            <td>基准</td>
                            <td>快2倍，体积小41%</td>
                        </tr>
                        <tr>
                            <td><strong>TypeScript</strong></td>
                            <td>支持一般</td>
                            <td>原生TypeScript支持</td>
                        </tr>
                    </table>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"我们项目用的是Vue3。相比Vue2，Vue3用Proxy实现响应式，性能更好。Composition API让逻辑复用更方便，代码组织更清晰。"</p>
                    
                    <h4>常见追问</h4>
                    <p><strong>Q：为什么Proxy比Object.defineProperty好？</strong></p>
                    <p>A：Proxy可以监听整个对象，而defineProperty只能监听单个属性。Proxy可以监听数组变化，defineProperty需要额外处理。</p>
                    
                    <p><strong>Q：什么是Composition API？</strong></p>
                    <p>A：把相关逻辑组织在一起的API。比如用户信息相关的状态、方法都在一个setup函数里，比Options API分散在data、methods更清晰。</p>
                </div>
            </div>

            <button class="collapsible">React - 核心概念</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>React核心：虚拟DOM + Hooks</h4>
                    <ul>
                        <li><strong>虚拟DOM</strong>：用JS对象表示DOM树，diff算法找出变化部分，只更新变化的DOM</li>
                        <li><strong>Hooks</strong>：函数组件也能有状态（useState）、副作用（useEffect）</li>
                        <li><strong>单向数据流</strong>：数据从父组件流向子组件，子组件通过回调函数通知父组件</li>
                    </ul>
                    
                    <h4>常用Hooks</h4>
                    <ul>
                        <li><strong>useState</strong>：状态管理，类似Vue的ref</li>
                        <li><strong>useEffect</strong>：副作用处理，类似Vue的watch</li>
                        <li><strong>useMemo</strong>：计算值缓存，避免重复计算</li>
                        <li><strong>useCallback</strong>：函数缓存，避免子组件不必要的重渲染</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"React通过虚拟DOM和diff算法优化DOM操作。Hooks让函数组件也能有状态和生命周期，代码更简洁。"</p>
                </div>
            </div>

            <button class="collapsible">前端工程化 - 基础</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>打包工具</h4>
                    <ul>
                        <li><strong>Webpack</strong>：老牌打包工具，功能强大但配置复杂</li>
                        <li><strong>Vite</strong>：现代化工具，开发时用ESM，生产用Rollup，速度快</li>
                    </ul>
                    
                    <h4>CSS方案</h4>
                    <ul>
                        <li><strong>Tailwind CSS</strong>：原子化CSS，写HTML就能加样式</li>
                        <li><strong>CSS Modules</strong>：CSS作用域隔离</li>
                    </ul>
                    
                    <h4>状态管理</h4>
                    <ul>
                        <li><strong>Vuex/Pinia</strong>：Vue的状态管理</li>
                        <li><strong>Redux</strong>：React的状态管理</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">TypeScript - 基础</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>为什么用TypeScript</h4>
                    <ul>
                        <li><strong>类型安全</strong>：编译时发现错误，减少运行时bug</li>
                        <li><strong>智能提示</strong>：IDE自动补全、重构更方便</li>
                        <li><strong>可维护性</strong>：大项目代码更容易理解</li>
                    </ul>
                    
                    <h4>基础类型</h4>
                    <pre>// 基本类型
let name: string = "张三";
let age: number = 18;
let isStudent: boolean = true;

// 数组
let list: number[] = [1, 2, 3];
let list2: Array&lt;number&gt; = [1, 2, 3];

// 接口
interface User {
    name: string;
    age: number;
    email?: string; // 可选属性
}

// 函数
function greet(user: User): string {
    return `Hello ${user.name}`;
}</pre>
                </div>

                <div class="answer-box">
                    <h4>面试这么说</h4>
                    <p>"项目用TypeScript提升代码质量。类型约束让团队协作更顺畅，大型项目重构也更安全。"</p>
                </div>
            </div>

            <button class="collapsible">前端性能优化 - 常见手段</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>优化策略</h4>
                    <ol>
                        <li><strong>懒加载</strong>：路由懒加载、图片懒加载</li>
                        <li><strong>代码分割</strong>：按需加载，减小首屏体积</li>
                        <li><strong>缓存</strong>：浏览器缓存、CDN缓存</li>
                        <li><strong>防抖节流</strong>：减少高频事件触发次数</li>
                        <li><strong>虚拟列表</strong>：大数据量渲染优化</li>
                        <li><strong>SSR</strong>：服务端渲染，提升首屏速度和SEO</li>
                    </ol>
                </div>
            </div>

            <button class="collapsible">HTTP & 浏览器 - 基础</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>HTTP状态码（必会）</h4>
                    <ul>
                        <li><strong>200</strong>：成功</li>
                        <li><strong>301/302</strong>：重定向（301永久，302临时）</li>
                        <li><strong>304</strong>：资源未修改，使用缓存</li>
                        <li><strong>400</strong>：请求参数错误</li>
                        <li><strong>401</strong>：未授权</li>
                        <li><strong>403</strong>：禁止访问</li>
                        <li><strong>404</strong>：资源不存在</li>
                        <li><strong>500</strong>：服务器内部错误</li>
                        <li><strong>502</strong>：网关错误</li>
                        <li><strong>503</strong>：服务不可用</li>
                    </ul>
                    
                    <h4>跨域解决方案</h4>
                    <ul>
                        <li><strong>CORS</strong>：后端设置Access-Control-Allow-Origin</li>
                        <li><strong>代理</strong>：开发环境用Vite/Webpack代理</li>
                        <li><strong>JSONP</strong>：只支持GET，已过时</li>
                    </ul>
                </div>
            </div>
        </div>

        <!-- Python基础知识 -->
        <div class="content" id="content5">
            <h2>🐍 Python基础知识</h2>
            
            <p class="warning-box">
                <strong>💡 说明：</strong>AI开发必备Python基础，覆盖高频面试题和深度技术点。
            </p>

            <button class="collapsible">⭐ Python数据类型与变量（高频）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>基本数据类型</h4>
                    <ul>
                        <li><strong class="term explain" data-pronunciation="ˈɪntɪdʒə" data-explain="整数类型：Python3中int无大小限制，自动处理大整数">int</strong>：整数，无大小限制</li>
                        <li><strong class="term explain" data-pronunciation="fləʊt" data-explain="浮点数：64位双精度，注意精度问题">float</strong>：浮点数（64位双精度）</li>
                        <li><strong class="term explain" data-pronunciation="strɪŋ" data-explain="字符串：不可变序列，支持Unicode">str</strong>：字符串（不可变）</li>
                        <li><strong class="term explain" data-pronunciation="ˈbuːliən" data-explain="布尔值：True或False，注意首字母大写">bool</strong>：布尔值（True/False）</li>
                        <li><strong class="term explain" data-pronunciation="nʌn taɪp" data-explain="空值：表示无值或缺失，是单例对象">None</strong>：空值（单例对象）</li>
                    </ul>
                    
                    <h4>容器类型</h4>
                    <ul>
                        <li><strong class="term explain" data-pronunciation="lɪst" data-explain="列表：可变有序序列，支持索引和切片">list</strong>：列表 <code>[1, 2, 3]</code>（可变）</li>
                        <li><strong class="term explain" data-pronunciation="ˈtjuːpl" data-explain="元组：不可变有序序列，常用于返回多个值">tuple</strong>：元组 <code>(1, 2, 3)</code>（不可变）</li>
                        <li><strong class="term explain" data-pronunciation="dɪkʃəneri" data-explain="字典：键值对映射，Python3.7+保持插入顺序">dict</strong>：字典 <code>{'key': 'value'}</code>（键值对）</li>
                        <li><strong class="term explain" data-pronunciation="set" data-explain="集合：无序不重复元素集，支持交并差运算">set</strong>：集合 <code>{1, 2, 3}</code>（无序不重复）</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>可变vs不可变对象</h4>
                    <p><strong>不可变：</strong>int, float, str, tuple, frozenset</p>
                    <pre>a = "hello"
a[0] = "H"  # ❌ TypeError: 不支持修改</pre>
                    
                    <p><strong>可变：</strong>list, dict, set</p>
                    <pre>lst = [1, 2, 3]
lst[0] = 10  # ✓ 可以修改</pre>
                    
                    <p><strong>⚠️ 面试陷阱：</strong></p>
                    <pre>def append_to(element, target=[]):  # ❌ 默认参数陷阱
    target.append(element)
    return target

print(append_to(1))  # [1]
print(append_to(2))  # [1, 2] ⚠️ 共享了同一个列表！

# 正确写法：
def append_to(element, target=None):
    if target is None:
        target = []
    target.append(element)
    return target</pre>
                </div>
            </div>

            <button class="collapsible">⭐ 列表推导式与迭代器（高频）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>列表推导式（List Comprehension）</h4>
                    <pre># 基本语法
squares = [x**2 for x in range(10)]

# 带条件
evens = [x for x in range(20) if x % 2 == 0]

# 嵌套
matrix = [[i+j for j in range(3)] for i in range(3)]
# [[0,1,2], [1,2,3], [2,3,4]]</pre>
                    
                    <h4>生成器表达式（Generator Expression）</h4>
                    <pre># 使用()而不是[]
gen = (x**2 for x in range(10))  # 惰性计算，节省内存
print(next(gen))  # 0
print(next(gen))  # 1</pre>
                    
                    <p><strong class="term explain" data-pronunciation="ˈdʒenəreɪtə" data-explain="生成器：使用yield关键字的函数，支持惰性计算和迭代">Generator</strong> vs List：生成器节省内存，适合大数据处理</p>
                </div>

                <div class="answer-box">
                    <h4>迭代器与可迭代对象</h4>
                    <p><strong class="term explain" data-pronunciation="ˈɪtərəbl" data-explain="可迭代对象：实现了__iter__方法，可以返回迭代器">Iterable</strong>：实现了 <code>__iter__()</code> 的对象</p>
                    <p><strong class="term explain" data-pronunciation="ˈɪtəreɪtə" data-explain="迭代器：实现了__iter__和__next__方法，可以逐个返回元素">Iterator</strong>：实现了 <code>__iter__()</code> 和 <code>__next__()</code></p>
                    
                    <pre># 自定义迭代器
class Countdown:
    def __init__(self, start):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        self.current -= 1
        return self.current + 1

# 使用
for num in Countdown(5):
    print(num)  # 5, 4, 3, 2, 1</pre>
                </div>
            </div>

            <button class="collapsible">⭐ 函数与作用域（高频）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>函数参数类型</h4>
                    <pre>def func(a, b=10, *args, **kwargs):
    """
    a: 位置参数
    b: 默认参数
    *args: 可变位置参数（元组）
    **kwargs: 可变关键字参数（字典）
    """
    pass

func(1, 2, 3, 4, x=5, y=6)
# a=1, b=2, args=(3,4), kwargs={'x':5, 'y':6}</pre>
                    
                    <h4>LEGB作用域规则</h4>
                    <ul>
                        <li><strong>L（Local）</strong>：局部作用域</li>
                        <li><strong>E（Enclosing）</strong>：嵌套函数的外层作用域</li>
                        <li><strong>G（Global）</strong>：全局作用域</li>
                        <li><strong>B（Built-in）</strong>：内置作用域</li>
                    </ul>
                    
                    <pre>x = "global"

def outer():
    x = "enclosing"
    
    def inner():
        x = "local"
        print(x)  # local
    
    inner()
    print(x)  # enclosing

outer()
print(x)  # global</pre>
                </div>

                <div class="warning-box">
                    <h4>闭包（Closure）</h4>
                    <pre>def outer(x):
    def inner(y):
        return x + y  # inner访问外层变量x
    return inner

add5 = outer(5)
print(add5(3))  # 8

# 查看闭包变量
print(add5.__closure__)  # (<cell at 0x...: int object at 0x...>,)</pre>
                    
                    <p><strong>⚠️ 常见陷阱：</strong></p>
                    <pre># ❌ 错误：循环中的闭包
funcs = []
for i in range(3):
    funcs.append(lambda: i)  # 所有lambda共享同一个i

for f in funcs:
    print(f())  # 2, 2, 2（都是最后的i值）

# ✓ 正确：使用默认参数
funcs = []
for i in range(3):
    funcs.append(lambda x=i: x)

for f in funcs:
    print(f())  # 0, 1, 2</pre>
                </div>
            </div>

            <button class="collapsible">🌟 装饰器（深度讲解）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>什么是装饰器？</h4>
                    <p><strong class="term explain" data-pronunciation="ˈdekəreɪtə" data-explain="装饰器：本质是闭包，用于在不修改原函数代码的情况下增加功能">装饰器</strong>是一个接受函数作为参数并返回新函数的<strong>高阶函数</strong>，用于在不修改原函数的情况下增加功能。</p>
                    
                    <h4>基础装饰器</h4>
                    <pre>import time

def timer(func):
    """计时装饰器"""
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end-start:.4f}s")
        return result
    return wrapper

@timer  # 等价于：slow_func = timer(slow_func)
def slow_func():
    time.sleep(1)
    return "done"

slow_func()  # slow_func took 1.0012s</pre>
                    
                    <h4>带参数的装饰器</h4>
                    <pre>def repeat(times):
    """重复执行装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(3)  # 三层嵌套
def say_hello():
    return "Hello"

print(say_hello())  # ['Hello', 'Hello', 'Hello']</pre>
                </div>

                <div class="answer-box">
                    <h4>装饰器执行顺序</h4>
                    <pre>def decorator1(func):
    print("decorator1")
    return func

def decorator2(func):
    print("decorator2")
    return func

@decorator1  # 第2个执行
@decorator2  # 第1个执行（从下往上）
def my_func():
    pass

# 输出：
# decorator2
# decorator1</pre>
                    
                    <p><strong>等价于：</strong></p>
                    <pre>my_func = decorator1(decorator2(my_func))</pre>
                </div>

                <div class="warning-box">
                    <h4>⚠️ 保留原函数信息</h4>
                    <pre>from functools import wraps

def my_decorator(func):
    @wraps(func)  # ✓ 保留原函数的__name__、__doc__等
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def my_function():
    """This is my function"""
    pass

print(my_function.__name__)  # my_function（而不是wrapper）
print(my_function.__doc__)   # This is my function</pre>
                    
                    <h4>实际应用场景</h4>
                    <ul>
                        <li>🔐 <strong>权限验证</strong>：@login_required</li>
                        <li>⏱️ <strong>性能监控</strong>：@timer</li>
                        <li>💾 <strong>缓存</strong>：@lru_cache</li>
                        <li>📝 <strong>日志记录</strong>：@log</li>
                        <li>🔄 <strong>重试机制</strong>：@retry</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">🌟 GIL全局解释器锁（深度讲解）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>什么是GIL？</h4>
                    <p><strong class="term explain" data-pronunciation="ˈɡləʊbl ɪnˈtɜːprɪtə lɒk" data-explain="全局解释器锁：CPython的实现机制，同一时刻只允许一个线程执行Python字节码">GIL (Global Interpreter Lock)</strong> 是CPython的全局解释器锁，确保同一时刻只有一个线程执行Python字节码。</p>
                    
                    <h4>为什么需要GIL？</h4>
                    <ul>
                        <li>✅ <strong>简化内存管理</strong>：引用计数不需要复杂的锁机制</li>
                        <li>✅ <strong>保护C扩展</strong>：许多C库不是线程安全的</li>
                        <li>❌ <strong>限制多核利用</strong>：多线程无法真正并行执行Python代码</li>
                    </ul>
                </div>

                <div class="warning-box">
                    <h4>GIL的影响</h4>
                    <pre># CPU密集型：受GIL限制
import threading

def cpu_bound():
    count = 0
    for _ in range(10**7):
        count += 1

# 多线程反而更慢（线程切换开销）
threads = [threading.Thread(target=cpu_bound) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()

# IO密集型：不受GIL影响
def io_bound():
    import time
    time.sleep(1)  # IO操作会释放GIL

# 多线程有效
threads = [threading.Thread(target=io_bound) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()  # 总耗时约1秒</pre>
                </div>

                <div class="answer-box">
                    <h4>如何绕过GIL？</h4>
                    <p><strong>1. 使用多进程（multiprocessing）</strong></p>
                    <pre>from multiprocessing import Process

def cpu_task():
    # 每个进程有独立的GIL
    pass

processes = [Process(target=cpu_task) for _ in range(4)]
for p in processes: p.start()
for p in processes: p.join()</pre>
                    
                    <p><strong>2. 使用Cython/C扩展</strong></p>
                    <pre># Cython可以释放GIL
from cython.parallel import prange

def parallel_loop():
    with nogil:  # 释放GIL
        for i in prange(1000):
            # C层面的并行计算
            pass</pre>
                    
                    <p><strong>3. 使用NumPy等C实现的库</strong></p>
                    <pre>import numpy as np
# NumPy的底层C代码可以释放GIL
arr = np.random.rand(10000, 10000)
result = arr @ arr  # 矩阵乘法在GIL外执行</pre>
                </div>

                <div class="warning-box">
                    <h4>面试高频问题</h4>
                    <p><strong>Q：多线程适用什么场景？</strong></p>
                    <p>A：<strong>IO密集型</strong>（网络请求、文件读写、数据库查询），因为IO操作会释放GIL。</p>
                    
                    <p><strong>Q：多进程适用什么场景？</strong></p>
                    <p>A：<strong>CPU密集型</strong>（数值计算、图像处理、数据分析），每个进程有独立的GIL。</p>
                    
                    <p><strong>Q：为什么不移除GIL？</strong></p>
                    <p>A：移除GIL会导致单线程性能下降（需要更多锁），且破坏大量现有C扩展的兼容性。</p>
                </div>
            </div>

            <button class="collapsible">🌟 生成器与yield（深度讲解）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>生成器的本质</h4>
                    <p><strong class="term explain" data-pronunciation="jiːld" data-explain="yield关键字：暂停函数执行并返回值，保存函数状态，下次调用时恢复">yield</strong> 关键字将普通函数变成生成器函数，支持惰性计算和状态保存。</p>
                    
                    <pre># 普通函数：一次性返回所有结果
def get_numbers():
    return [0, 1, 2, 3, 4]

# 生成器函数：逐个产生结果
def generate_numbers():
    for i in range(5):
        yield i  # 暂停并返回i，保存状态

gen = generate_numbers()
print(next(gen))  # 0
print(next(gen))  # 1</pre>
                    
                    <h4>生成器的优势</h4>
                    <ul>
                        <li>💾 <strong>节省内存</strong>：不一次性加载所有数据</li>
                        <li>⚡ <strong>延迟计算</strong>：只在需要时计算</li>
                        <li>∞ <strong>无限序列</strong>：可表示无限长的序列</li>
                    </ul>
                    
                    <pre># 无限序列
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
print([next(fib) for _ in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]</pre>
                </div>

                <div class="answer-box">
                    <h4>yield from（Python 3.3+）</h4>
                    <pre># 不使用yield from
def chain(*iterables):
    for iterable in iterables:
        for item in iterable:
            yield item

# 使用yield from
def chain(*iterables):
    for iterable in iterables:
        yield from iterable  # 委托给子生成器

list(chain([1,2], [3,4], [5,6]))  # [1,2,3,4,5,6]</pre>
                    
                    <h4>生成器的三种操作</h4>
                    <pre>def my_generator():
    x = yield 1  # 1. 产生值
    print(f"Received: {x}")
    x = yield 2
    print(f"Received: {x}")

gen = my_generator()
print(next(gen))        # 1
print(gen.send(10))     # Received: 10, 输出2
gen.close()             # 关闭生成器</pre>
                </div>

                <div class="warning-box">
                    <h4>实际应用：大文件处理</h4>
                    <pre># ❌ 不推荐：一次性读取（内存爆炸）
def read_file_bad(filename):
    with open(filename) as f:
        return f.readlines()  # 加载整个文件到内存

# ✓ 推荐：使用生成器
def read_file_good(filename):
    with open(filename) as f:
        for line in f:
            yield line.strip()  # 逐行处理

# 处理大文件
for line in read_file_good('large_file.txt'):
    process(line)  # 内存占用恒定</pre>
                    
                    <h4>生成器表达式vs列表推导式</h4>
                    <pre># 列表推导式：立即计算，占用内存
squares_list = [x**2 for x in range(1000000)]  # 创建100万元素的列表

# 生成器表达式：惰性计算，节省内存
squares_gen = (x**2 for x in range(1000000))   # 仅存储生成规则

# 如果只需遍历一次，用生成器
sum(squares_gen)  # 计算平方和，内存占用很小</pre>
                </div>
            </div>

            <button class="collapsible">⭐ 异常处理与上下文管理器</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>异常处理</h4>
                    <pre>try:
    risky_operation()
except ValueError as e:
    print(f"ValueError: {e}")
except (TypeError, KeyError):
    print("Type or Key error")
except Exception as e:  # 捕获所有异常
    print(f"Unexpected: {e}")
else:
    print("No exception occurred")
finally:
    print("Always execute")  # 清理资源</pre>
                    
                    <h4>自定义异常</h4>
                    <pre>class InvalidAgeError(Exception):
    """年龄无效异常"""
    pass

def set_age(age):
    if age < 0:
        raise InvalidAgeError("年龄不能为负数")
    return age</pre>
                </div>

                <div class="answer-box">
                    <h4>上下文管理器（with语句）</h4>
                    <pre># 自动资源管理
with open('file.txt') as f:
    data = f.read()
# 文件自动关闭，即使发生异常

# 自定义上下文管理器
class Timer:
    def __enter__(self):
        self.start = time.time()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.time()
        print(f"Time: {self.end - self.start}")
        return False  # False表示不压制异常

with Timer():
    time.sleep(1)  # Time: 1.0012</pre>
                    
                    <p><strong>使用contextlib简化</strong></p>
                    <pre>from contextlib import contextmanager

@contextmanager
def timer():
    start = time.time()
    yield
    end = time.time()
    print(f"Time: {end - start}")

with timer():
    time.sleep(1)</pre>
                </div>
            </div>

            <button class="collapsible">⭐ 面向对象编程（OOP）</button>
            <div class="collapsible-content">
                <div class="tech-box">
                    <h4>类与对象</h4>
                    <pre>class Person:
    species = "Human"  # 类属性
    
    def __init__(self, name, age):
        self.name = name  # 实例属性
        self.age = age
    
    def greet(self):  # 实例方法
        return f"Hi, I'm {self.name}"
    
    @classmethod
    def from_birth_year(cls, name, birth_year):
        return cls(name, 2025 - birth_year)
    
    @staticmethod
    def is_adult(age):
        return age >= 18

p = Person("Alice", 25)
print(p.greet())  # Hi, I'm Alice</pre>
                    
                    <h4>继承与多态</h4>
                    <pre>class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id
    
    def greet(self):  # 方法重写
        return f"Hi, I'm student {self.name}"

s = Student("Bob", 20, "S001")
print(s.greet())  # Hi, I'm student Bob</pre>
                </div>

                <div class="answer-box">
                    <h4>魔术方法（Dunder Methods）</h4>
                    <pre>class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y
    
    def __str__(self):  # print()时调用
        return f"Vector({self.x}, {self.y})"
    
    def __repr__(self):  # 交互式环境显示
        return f"Vector({self.x}, {self.y})"
    
    def __add__(self, other):  # +运算符
        return Vector(self.x + other.x, self.y + other.y)
    
    def __len__(self):
        return int((self.x**2 + self.y**2)**0.5)

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)  # Vector(4, 6)</pre>
                    
                    <p><strong>常用魔术方法：</strong></p>
                    <ul>
                        <li><code>__init__</code>：构造函数</li>
                        <li><code>__str__/__repr__</code>：字符串表示</li>
                        <li><code>__len__/__getitem__</code>：支持len()和[]</li>
                        <li><code>__enter__/__exit__</code>：上下文管理器</li>
                        <li><code>__call__</code>：使对象可调用</li>
                    </ul>
                </div>
            </div>

        </div>

        <!-- 项目介绍话术 -->
        <div class="content" id="content6">
            <h2>💼 项目介绍话术</h2>

            <p class="warning-box">
                <strong>💡 自我介绍专用：</strong>用大白话把项目讲清楚，让面试官听懂你做了什么。语气要自然、接地气。
            </p>

            <div class="highlight-box">
                <h3>开场白（30秒）</h3>
                <p>"您好，我是任相鹏，8 年工作经验，主要做大数据开发和 AI 相关的工作。最近两年主要在做 AI 大模型相关的项目，之前几年做的是大数据实时计算和集群运维。我擅长把开源大模型本地化部署，做智能问答、智能体这些应用，也擅长 Flink 实时计算、性能优化这些。下面我简单介绍一下几个代表性的项目。"</p>
            </div>

            <hr>

            <button class="collapsible">⭐ 项目一：SuperAgent 智能体平台（最新项目，重点讲）</button>
            <div class="collapsible-content">
                <h4>基础版（1分钟）</h4>
                <div class="example-box">
                    <p>"最近在做的是一个通用智能体平台，叫 SuperAgent。这个项目是给军工企业用的，因为是内网环境，数据不能出去，所以我们用的都是开源大模型本地部署。</p>
                    <p>这个平台的核心思路是这样的：用户说一句话，比如'帮我生成一份项目汇报 PPT'，我们有个决策智能体会把这个需求拆解成多个子任务，然后调度不同的专业智能体去完成。它们之间通过 Google 的 A2A 协议互相配合工作。</p>
                    <p>整个执行过程对用户是透明可见的，用户能看到每一步在干什么，哪个智能体在工作。而且我们提供了语音交互，用户可以直接说话，系统实时识别转成文字。"</p>
                </div>

                <h4>关键技术点（面试深问时）</h4>
                <div class="answer-box">
                    <h4>1. 多智能体协作</h4>
                    <ul>
                        <li>决策智能体：任务拆解和调度</li>
                        <li>专业智能体：PPT 生成、文档处理、代码执行等</li>
                        <li>模型选择：对话用 Qwen3-14B，代码生成用 Qwen3-Coder-7B，复杂推理用 DeepSeek-V3</li>
                    </ul>

                    <h4>2. 大模型部署和优化</h4>
                    <ul>
                        <li><strong>硬件环境</strong>：华为昇腾 910B，每张卡 64G 显存，8 卡服务器</li>
                        <li><strong>推理框架</strong>：vLLM，吞吐量提升 3-4 倍</li>
                        <li><strong>模型量化</strong>：INT8 量化，显存占用减半</li>
                        <li><strong>性能指标</strong>：首字延迟 500ms 以内，吞吐量 50+ 并发</li>
                    </ul>

                    <h4>3. 国产显卡适配（面试加分项）</h4>
                    <ul>
                        <li>vLLM 昇腾适配：替换 CUDA 调用为 CANN 接口</li>
                        <li>算子优化：针对 Attention、LayerNorm、SoftMax 等关键算子</li>
                        <li>显存管理：优化显存分配策略，充分利用 64G 显存</li>
                        <li>兼容性测试：模型输出精度误差 < 0.1%</li>
                    </ul>
                </div>

                <h4>常见追问</h4>
                <div class="warning-box">
                    <p><strong>Q：为什么选 vLLM？</strong></p>
                    <p>A：vLLM 的核心优势是推理性能。它用了 PagedAttention 技术，把 KV Cache 像操作系统分页内存一样管理，显存利用率高。还有 Continuous Batching，动态合并请求，吞吐量能提升 2-3 倍。</p>
                    
                    <p><strong>Q：模型升级怎么做的？</strong></p>
                    <p>A：从 <span class="term" data-pronunciation="Q问2.5">Qwen2.5<span class="pronun-icon">🔊</span></span> 升级到 <span class="term" data-pronunciation="Q问3">Qwen3<span class="pronun-icon">🔊</span></span> 主要做了三件事：一是模型权重转换，二是 <span class="term" data-pronunciation="撇破母特">Prompt<span class="pronun-icon">🔊</span></span> 模板适配，三是推理参数调优（temperature、top_p 等）。升级后任务拆解的准确性明显提升。</p>
                </div>
            </div>

            <button class="collapsible">⭐ 项目二：企业智能问答与数据分析平台</button>
            <div class="collapsible-content">
                <h4>基础版（1分钟）</h4>
                <div class="example-box">
                    <p>"在这之前做的项目也是 AI 相关的，是个企业智能问答平台，也是给军工企业用的。这个平台整合了企业内部的文档、数据库、业务系统等多源数据，用户可以用自然语言提问，系统自动回答或者查数据。</p>
                    <p>比如用户问'2024 年第一季度销售额是多少'，如果知识库里有相关文档，就用 RAG（检索增强生成）的方式回答；如果需要查数据库，系统会自动生成 SQL 去查，然后把结果用图表展示出来。"</p>
                </div>

                <h4>关键技术点</h4>
                <div class="answer-box">
                    <h4>1. RAG 文档问答</h4>
                    <p><strong>核心流程：</strong>文档解析 → 切片 → 向量化 → 检索 → 生成答案</p>
                    <ul>
                        <li>向量化：BGE-M3 模型（1024 维向量）</li>
                        <li>混合检索：向量检索 + BM25 关键词检索</li>
                        <li>重排序：gte-Qwen2-7B 精排</li>
                        <li>性能：毫秒级检索百万级文档</li>
                    </ul>

                    <h4>2. Text2SQL 智能查询</h4>
                    <ul>
                        <li><strong>使用模型</strong>：Qwen2.5-Coder-7B</li>
                        <li><strong>准确率</strong>：简单查询 95%+，复杂查询 80%+</li>
                        <li><strong>安全机制</strong>：禁止危险操作，自动加 LIMIT</li>
                    </ul>

                    <h4>3. 推理性能优化</h4>
                    <ul>
                        <li>vLLM 推理框架：PagedAttention + Continuous Batching</li>
                        <li>模型量化：INT8 量化</li>
                        <li>向量索引：HNSW 算法</li>
                        <li>缓存机制：相似问题（余弦相似度 > 0.95）直接返回缓存</li>
                        <li>流式输出：SSE/WebSocket，首字延迟 500ms 以内</li>
                    </ul>
                </div>

                <h4>常见追问</h4>
                <div class="warning-box">
                    <p><strong>Q：RAG 怎么保证答案准确？</strong></p>
                    <p>A：主要靠检索质量。我们做了多路召回（向量 + 关键词），然后二阶段重排序，保证检索到的文档真的相关。另外模型生成答案时要求严格基于给定文档回答，不能瞎编，而且必须标注引用来源。</p>
                    
                    <p><strong>Q：Text2SQL 准确率怎么样？</strong></p>
                    <p>A：经过优化后，简单查询准确率能到 95%+，复杂查询也能达到 80%+。主要优化手段是 Few-shot 示例库，我们收集了几百个典型问题和对应的 SQL，根据用户问题找最相似的示例给模型参考。</p>
                </div>
            </div>

            <button class="collapsible">⭐ 项目三：某中央银行实时数据湖平台（大数据核心项目）</button>
            <div class="collapsible-content">
                <h4>基础版（1分钟）</h4>
                <div class="example-box">
                    <p>"再往前是在银行做实时数据处理平台，这是个核心业务系统，承载全行的交易计算、风控监测、监管报送等业务。</p>
                    <p>这个平台每天要处理 5000 多万笔交易流水，数据量 2TB+，要求是秒级响应。我们用 Flink 做实时计算，消费 Kafka 的交易数据，做实时指标计算、异常检测等，结果写到 Redis 和 Oracle 供业务系统查询。</p>
                    <p>同时我们还构建了基于 Hudi 的实时数据湖，Flink 把数据实时入湖到 HDFS，支持流批一体查询。"</p>
                </div>

                <h4>关键技术点</h4>
                <div class="answer-box">
                    <h4>1. Flink 实时计算</h4>
                    <ul>
                        <li>管理 TB 级状态数据（客户资产、交易历史、风险评分等）</li>
                        <li>RocksDB 状态后端，支持增量 Checkpoint</li>
                        <li>Checkpoint 配置：每 5 分钟一次，最小间隔 2 分钟，超时 15 分钟</li>
                    </ul>

                    <h4>2. 性能优化（面试重点）</h4>
                    <ul>
                        <li>Kafka 分区扩容：从 64 分区扩到 256 分区</li>
                        <li>Flink 并行度：从 32 调到 128</li>
                        <li>算子链优化、异步 IO、窗口聚合提前</li>
                        <li>Checkpoint 优化：时间从 20 分钟降到 5 分钟</li>
                        <li>资源调优：TaskManager 堆内 8GB + 堆外 4GB + RocksDB 缓存 2GB</li>
                    </ul>
                    <p><strong>优化效果：</strong>处理延迟从小时级降到秒级，Checkpoint 成功率从 70% 提升到 99%+</p>

                    <h4>3. 实时数据湖</h4>
                    <ul>
                        <li><strong>Copy-On-Write 表</strong>：存实时增量数据，分钟级更新</li>
                        <li><strong>Merge-On-Read 表</strong>：处理 CDC 数据，Compaction 合并小文件</li>
                        <li><strong>分区策略</strong>：按日期 + Hash 二级分区，避免数据倾斜</li>
                        <li><strong>元数据同步</strong>：Hudi 自动同步到 Hive Metastore</li>
                    </ul>
                </div>

                <h4>常见追问</h4>
                <div class="warning-box">
                    <p><strong>Q：TB 级状态怎么管理的？</strong></p>
                    <p>A：主要靠 RocksDB 增量 Checkpoint。RocksDB 是 LSM Tree 结构，每次 Checkpoint 只保存变化的数据（增量），不用全量保存，大大减少了 Checkpoint 时间和存储开销。</p>
                    
                    <p><strong>Q：遇到过什么故障？</strong></p>
                    <p>A：印象深刻的是有一次 HDFS NameNode 主备切换，导致 Flink Checkpoint 失败，作业大量重启。我们排查后发现是 Checkpoint 超时时间设置得太短，后来把超时时间从 10 分钟调到 15 分钟，而且配置了 Checkpoint 失败容忍次数，允许连续失败 3 次，避免偶发故障导致作业频繁重启。"</p>
                </div>
            </div>

            <button class="collapsible">项目四：自研纯Java大数据平台</button>
            <div class="collapsible-content">
                <h4>基础版（30秒）</h4>
                <div class="example-box">
                    <p>"我还参与过一个自研大数据平台的开发，是用纯 Java 做的云原生集群管理平台。这个平台可以一键部署和管理 Hadoop 生态的各种组件，支持 K8s 容器化部署，也支持裸机部署。</p>
                    <p>我主要负责组件集成、安全认证（Kerberos、Ranger）、国产化适配（达梦数据库、飞腾 CPU）这些工作。"</p>
                </div>

                <h4>关键技术点</h4>
                <div class="answer-box">
                    <ul>
                        <li><strong>组件一键化管理</strong>：Kibana、Kyuubi、Tez、Zeppelin、Phoenix、Doris、StarRocks 等组件的安装、配置、监控</li>
                        <li><strong>K8s 编排</strong>：通过 ConfigMap/Service/PVC 管理组件配置和持久化</li>
                        <li><strong>安全治理</strong>：Kerberos 全链路接入、Ranger 权限管理、达梦数据库适配</li>
                        <li><strong>实时日志</strong>：SSE 推送安装和运行日志，过程可视化</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">项目五：大数据基础平台（HDP二开）</button>
            <div class="collapsible-content">
                <h4>基础版（30秒）</h4>
                <div class="example-box">
                    <p>"2020年做过一个基于 HDP 二次开发的项目，主要是国产化适配。HDP 是 Hortonworks 的 Hadoop 发行版，我们需要让它支持 ARM 架构（飞腾 CPU）和国产数据库（达梦）。</p>
                    <p>我负责 Hive Metastore 的达梦适配，包括 DDL 语法改造、数据类型映射等，还做了 ClickHouse 的飞腾适配，涉及源码级改造。"</p>
                </div>

                <h4>关键技术点</h4>
                <div class="answer-box">
                    <ul>
                        <li><strong>ARM 架构适配</strong>：x86 到 ARM 的跨架构编译，JVM 调优</li>
                        <li><strong>达梦数据库适配</strong>：Hive Metastore 支持达梦，解决 SQL 方言兼容</li>
                        <li><strong>高版本组件集成</strong>：集成 Flink 1.15+、Kudu、Alluxio、MinIO</li>
                        <li><strong>国产 OS 兼容</strong>：麒麟 V10、中标麒麟等国产操作系统适配</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">项目六：舆情大数据分析系统</button>
            <div class="collapsible-content">
                <h4>基础版（30秒）</h4>
                <div class="example-box">
                    <p>"最早是做互联网舆情监控的，7x24小时采集论坛、微博、新闻这些公开数据，用 NLP 技术做情感分析、敏感词识别、热点挖掘。</p>
                    <p>我经历了流式计算的技术升级：2016-2018 用的是 Storm，2018 年开始我主导升级到 Flink，系统延迟从分钟级降到秒级，稳定性大幅提升。"</p>
                </div>

                <h4>关键技术点</h4>
                <div class="answer-box">
                    <ul>
                        <li><strong>技术升级</strong>：Storm → Flink 迁移，延迟降低、稳定性从 99.5% 提升到 99.9%</li>
                        <li><strong>冷热分离</strong>：MySQL 存 7 天热数据，HBase 存历史数据</li>
                        <li><strong>HBase 优化</strong>：RowKey 设计（地域 + 时间戳反转），查询性能提升 3 倍</li>
                        <li><strong>NLP 处理</strong>：jieba 分词、SnowNLP 情感分析、TextRank 关键词提取</li>
                        <li><strong>实时预警</strong>：邮件、短信、弹窗推送，响应时间 < 30秒</li>
                    </ul>
                </div>
            </div>

            <hr>

            <h3>面试技巧</h3>
            <div class="warning-box">
                <h4>讲项目的节奏</h4>
                <ul>
                    <li><strong>第一层（30秒）</strong>：项目背景 + 核心功能 + 关键技术</li>
                    <li><strong>第二层（1-2分钟）</strong>：技术架构 + 实现方案 + 技术亮点</li>
                    <li><strong>第三层（看面试官反应）</strong>：性能数据 + 遇到的问题 + 如何解决</li>
                </ul>
                <p><strong>不要一上来就讲细节！</strong>先让面试官知道你做了什么，有什么价值，再根据他的兴趣点展开。</p>
            </div>

            <div class="highlight-box">
                <h4>项目选择策略</h4>
                <ul>
                    <li><strong>面试官如果偏 AI</strong>：重点讲 SuperAgent 和智能问答平台，强调大模型部署、RAG、Text2SQL、性能优化</li>
                    <li><strong>面试官如果偏大数据</strong>：重点讲银行项目，强调 Flink 性能优化、状态管理、实时数据湖</li>
                    <li><strong>面试官如果关注国产化</strong>：重点讲昇腾显卡适配、达梦数据库适配、ARM 架构适配</li>
                </ul>
            </div>

            <div class="example-box">
                <h4>加分项</h4>
                <ul>
                    <li><strong>数据说话</strong>：性能提升 X 倍、延迟降低到 X 秒、准确率达到 X%</li>
                    <li><strong>踩坑经验</strong>：遇到了 XX 问题，怎么排查的，怎么解决的</li>
                    <li><strong>技术演进</strong>：从 A 技术升级到 B 技术，为什么，效果如何</li>
                    <li><strong>系统思维</strong>：不只是做功能，还考虑性能、稳定性、可维护性、成本</li>
                </ul>
            </div>
        </div>

        <!-- 应对策略 -->
        <div class="content" id="content6">
            <h2>🎯 面试应对策略</h2>

            <p class="warning-box">
                <strong>⚠️ 重要提示：</strong>这部分是面试核心应对策略，帮你应对可能暴露经验不足的追问。记住关键点，灵活应对。
            </p>

            <button class="collapsible">⭐ 银行业务场景应对（核心）</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>面临的问题</h4>
                    <p>你在银行项目中主要负责技术实现，对具体业务场景了解有限。面试官可能会追问"处理哪些业务场景？给我举个例子？"</p>
                    
                    <h4>应对策略</h4>
                    <p><strong>不要说"我只负责技术，不了解业务"！</strong>这样会让面试官觉得你只是个工具人，没有业务理解能力。</p>
                    <p><strong>正确做法：</strong>准备 3-4 个典型业务场景，用技术视角描述，体现你对业务的理解。</p>
                </div>

                <div class="answer-box">
                    <h4>场景1：<span class="explain" data-explain="就是核对账目，看账对不对得上。比如你转账100元，交易系统记录100，支付网关也要记录100，三方数据要对得上">交易对账<span class="explain-icon">❓</span></span></h4>
                    <p><strong>业务描述：</strong>"我们处理的一个重要场景是交易对账。银行每天有大量跨行转账、网银支付等交易，这些交易会经过多个系统，需要实时核对是否一致。"</p>
                    <p><strong>技术实现：</strong></p>
                    <ul>
                        <li>Kafka 接收<span class="explain" data-explain="银行内部处理交易的系统，比如转账、取款等">交易系统<span class="explain-icon">❓</span></span>、<span class="explain" data-explain="连接外部支付通道的网关，比如微信、支付宝、银联">支付网关<span class="explain-icon">❓</span></span>、<span class="explain" data-explain="银行最核心的业务系统，存储账户余额、交易记录等关键数据">核心系统<span class="explain-icon">❓</span></span>的交易流水</li>
                        <li>Flink 按交易号 JOIN 三方数据，实时比对金额、时间、状态</li>
                        <li>如果发现不一致（<span class="explain" data-explain="账对不上就叫差错，比如交易系统记100，核心系统记99，这就是差错">差错<span class="explain-icon">❓</span></span>），立即写入 Redis 触发告警</li>
                        <li>对账结果写入 Oracle 供业务系统查询</li>
                    </ul>
                    <p><strong>数据量：</strong>日均 5000 万+ 笔交易，峰值 <span class="term explain" data-pronunciation="Q-P-S" data-explain="Queries Per Second，每秒查询数，这里指每秒处理3000笔交易">QPS<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 3000+</p>
                </div>

                <div class="answer-box">
                    <h4>场景2：<span class="explain" data-explain="防止通过银行转账洗黑钱。洗钱就是把不合法的钱（比如贪污的）通过各种交易变成合法的钱">反洗钱监测<span class="explain-icon">❓</span></span></h4>
                    <p><strong>业务描述：</strong>"还有个重要场景是反洗钱监测。监管要求银行实时识别可疑交易，比如大额交易、频繁转账等。"</p>
                    <p><strong>技术实现：</strong></p>
                    <ul>
                        <li>Flink 实时消费交易流水</li>
                        <li>维护每个账户的<span class="explain" data-explain="记住每个账户的历史数据，比如今天转了多少钱、给谁转的。这些数据存在内存或RocksDB里">状态<span class="explain-icon">❓</span></span>（24小时累计交易金额、交易笔数、<span class="explain" data-explain="转账的另一方。比如你给张三转账，张三就是对手方。如果1小时内给10个不同的人转账，就很可疑">对手方<span class="explain-icon">❓</span></span>数量）</li>
                        <li>触发规则：
                            <ul>
                                <li>单笔 > 50 万 → 立即告警</li>
                                <li>24 小时累计 > 200 万 → 告警</li>
                                <li>1 小时内对手方 > 10 个 → 告警</li>
                            </ul>
                        </li>
                        <li>告警记录写入 Oracle，业务人员审核</li>
                    </ul>
                    <p><strong>技术难点：</strong>需要管理大量状态（几千万账户），用 <span class="explain" data-explain="为什么用RocksDB？因为状态数据太大，内存放不下。RocksDB是个嵌入式KV数据库，可以把数据存磁盘，而且支持增量Checkpoint（只保存变化的数据）">RocksDB<span class="explain-icon">❓</span></span> 存储</p>
                </div>

                <div class="answer-box">
                    <h4>场景3：<span class="explain" data-explain="银行必须定期向人民银行（央行）报送经营数据。1104是报表编号，主要统计贷款相关数据">监管报送（1104 报表）<span class="explain-icon">❓</span></span></h4>
                    <p><strong>业务描述：</strong>"银行需要定期向人民银行报送数据，比如 1104 报表（贷款统计报表），要求数据准确、及时。"</p>
                    <p><strong>技术实现：</strong></p>
                    <ul>
                        <li><span class="explain" data-explain="为什么用Flink CDC？因为需要实时同步数据库变化。CDC能捕获数据库的增删改操作，保证数据湖和业务库数据一致">Flink CDC<span class="explain-icon">❓</span></span> 实时同步核心业务库（贷款表、客户表）到数据湖</li>
                        <li><span class="explain" data-explain="为什么用Hudi？因为Hudi支持时间旅行，可以查询任意历史时点的数据。比如查'2024年1月1日的贷款余额'">Hudi<span class="explain-icon">❓</span></span> 存储历史快照，支持任意时点查询</li>
                        <li>Spark SQL 定时跑批（每月初），按监管要求统计：
                            <ul>
                                <li><span class="explain" data-explain="还没还完的贷款总金额">贷款余额<span class="explain-icon">❓</span></span>（按行业、地区、期限分类）</li>
                                <li><span class="explain" data-explain="还不上钱的贷款占比。比如总贷款100亿，有5亿还不上，不良率就是5%">不良贷款率<span class="explain-icon">❓</span></span></li>
                                <li><span class="explain" data-explain="最大客户的贷款占总贷款的比例。如果一个客户占比太高（比如50%），风险很大，鸡蛋不能放一个篮子里">贷款集中度<span class="explain-icon">❓</span></span></li>
                            </ul>
                        </li>
                        <li>生成标准格式文件，自动上报监管平台</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>场景4：客户风险评分</h4>
                    <p><strong>业务描述：</strong>"银行需要实时计算客户信用评分，用于<span class="explain" data-explain="决定要不要借钱给客户，借多少">贷款审批<span class="explain-icon">❓</span></span>、<span class="explain" data-explain="根据客户信用好坏，调整信用卡额度或贷款额度">额度调整<span class="explain-icon">❓</span></span>等。"</p>
                    <p><strong>技术实现：</strong></p>
                    <ul>
                        <li>Flink 实时消费客户行为数据（交易、还款、账户余额等）</li>
                        <li>基于<span class="explain" data-explain="简单的打分规则，不是复杂的机器学习模型。4年经验能做的就是规则引擎，不要说机器学习">规则模型<span class="explain-icon">❓</span></span>实时打分（逾期 -10 分，按时还款 +5 分，余额增加 +3 分）</li>
                        <li>维护客户维度状态（历史评分、最近 30 天行为）</li>
                        <li>评分变化超过<span class="explain" data-explain="设定的临界值，比如评分从80降到60（变化20分），超过阈值10，就触发告警">阈值<span class="explain-icon">❓</span></span>，触发事件通知风控系统</li>
                    </ul>
                </div>

                <div class="warning-box">
                    <h4>面试技巧</h4>
                    <ul>
                        <li><strong>不要背业务术语</strong>：用大白话解释，比如"对账就是检查账对不对得上"</li>
                        <li><strong>强调技术实现</strong>：业务说清楚就行，重点讲你怎么用技术解决的</li>
                        <li><strong>准备1-2个场景</strong>：不用全背，记住核心逻辑，灵活组合</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">⭐ 大数据量处理经验应对</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>面临的问题</h4>
                    <p>简历写了"日均 5000 万笔"，面试官可能追问"每秒多少？峰值多少？怎么保证处理得过来？"</p>
                    
                    <h4>应对策略</h4>
                    <p><strong>不要说精确数字！</strong>你不可能记得那么清楚，说错了反而露馅。</p>
                    <p><strong>正确做法：</strong>说范围、说流程、说优化手段，体现你知道怎么应对大数据量。</p>
                </div>

                <div class="answer-box">
                    <h4>数据量怎么说</h4>
                    <p><strong>不要说：</strong>"每秒 347 笔，峰值 1258 笔"（太精确，容易穿帮）</p>
                    <p><strong>应该说：</strong>"日均 5000 万笔，平均每秒几百笔，高峰期能到每秒 2000-3000 笔"</p>
                    <p><strong>万能话术：</strong>"我们是按日均量来规划的，实际处理能力留了 2-3 倍冗余，高峰期完全没问题。"</p>
                </div>

                <div class="answer-box">
                    <h4>怎么保证处理得过来（面试重点）</h4>
                    <p><strong>强调流程，不是死记数字：</strong></p>
                    <ol>
                        <li><strong>Kafka 缓冲</strong>：上游系统先写 Kafka，Kafka 有 7 天保留期，Flink 消费不过来也不丢数据</li>
                        <li><strong>弹性扩容</strong>：Flink on K8s，根据 Kafka 消费 Lag 自动扩容 Pod</li>
                        <li><strong>分区并行</strong>：Kafka 256 分区 + Flink 128 并行度，充分利用集群资源</li>
                        <li><strong>反压机制</strong>：下游处理慢了，Flink 自动降速，防止系统被打垮</li>
                        <li><strong>监控告警</strong>：Prometheus 监控消费 Lag，超过 5 分钟告警，提前扩容</li>
                    </ol>
                    <p><strong>总结：</strong>"我们关注的不是瞬时峰值，而是持续处理能力。通过 Kafka 削峰 + Flink 弹性扩容 + 监控告警，保证数据不积压。"</p>
                </div>

                <div class="answer-box">
                    <h4>性能调优怎么说</h4>
                    <p><strong>不要说：</strong>"调了某个参数从 X 变成 Y"（太细节，说不清楚）</p>
                    <p><strong>应该说：</strong>优化思路 + 效果对比</p>
                    <ol>
                        <li><strong><span class="explain" data-explain="为什么扩分区？Kafka分区是并行的基本单位。64分区→Flink最多64并行度→服务器128核CPU没用满。扩到256分区→Flink 128并行度→CPU利用率提升">Kafka 分区扩容<span class="explain-icon">❓</span></span></strong>：从 64 扩到 256，吞吐量提升 2-3 倍</li>
                        <li><strong><span class="explain" data-explain="为什么调并行度？并行度=同时处理的任务数。太小：CPU闲置。太大：内存不够。一般设置为CPU核数的1-2倍（这里128核→并行度128）">Flink 并行度调整<span class="explain-icon">❓</span></span></strong>：从 32 调到 128，充分利用集群资源</li>
                        <li><strong><span class="explain" data-explain="为什么Checkpoint慢？TB级状态全量保存太慢。RocksDB增量Checkpoint只保存变化的数据（像git commit），大幅减少时间和存储">Checkpoint 优化<span class="explain-icon">❓</span></span></strong>：RocksDB 增量 Checkpoint，时间从 20 分钟降到 5 分钟</li>
                        <li><strong><span class="explain" data-explain="为什么要算子链？Flink任务=多个算子组成。每个算子独立运行→数据要序列化网络传输（慢）。算子链合并→数据在内存直接传递（快）">算子链优化<span class="explain-icon">❓</span></span></strong>：合并可以合并的算子，减少网络开销</li>
                    </ol>
                    <p><strong>效果：</strong>"优化前处理延迟几十分钟，优化后降到秒级，Checkpoint 成功率从 70% 提升到 99%+"</p>
                </div>

                <div class="example-box">
                    <h4>万能应对话术</h4>
                    <p>"我们的处理逻辑是这样的：先通过 Kafka 做缓冲削峰，然后 Flink 实时消费做计算。数据量大的时候，重点是保证系统稳定性，而不是追求极致的延迟。</p>
                    <p>我们做了几点优化：一是 Kafka 分区扩容到 256，二是 Flink 并行度调到 128，三是 Checkpoint 优化，四是配置了自动扩缩容。这样高峰期也能稳定运行，消费延迟控制在秒级。"</p>
                </div>

                <div class="answer-box">
                    <h4>如果追问"有没有遇到数据倾斜"</h4>
                    <p><strong>什么是数据倾斜：</strong>"就是数据分布不均匀，某些分区或节点处理的数据特别多，成为瓶颈。"</p>
                    
                    <p><strong>具体场景：</strong></p>
                    <ul>
                        <li><strong>场景1：Kafka 分区倾斜</strong>
                            <ul>
                                <li><strong>现象：</strong>"某些分区积压严重，其他分区很空闲"</li>
                                <li><strong>原因：</strong>"用交易号做 Hash 分区，但某些商户交易特别多"</li>
                                <li><strong>解决：</strong>"改用复合 Key（商户ID + 交易时间），打散热点数据"</li>
                            </ul>
                        </li>
                        <li><strong>场景2：Flink 状态倾斜</strong>
                            <ul>
                                <li><strong>现象：</strong>"按账户 keyBy，某些账户（比如公司对公账户）交易特别多，状态特别大"</li>
                                <li><strong>解决：</strong>"拆分大 Key：把单个账户按时间窗口切分（比如按小时），或者二次打散"</li>
                            </ul>
                        </li>
                    </ul>
                    
                    <p><strong>万能答案：</strong>"遇到过数据倾斜。我们通过监控发现某些分区消费慢，排查后发现是 Key 分布不均。最后改了分区策略，加了二次打散逻辑，倾斜基本解决了。"</p>
                </div>

                <div class="answer-box">
                    <h4>如果追问"TB 级状态怎么管理"</h4>
                    <p><strong>背景说明：</strong>"银行项目需要维护几千万账户的状态（交易累计、余额变化等），状态数据达到 TB 级别。"</p>
                    
                    <p><strong>技术方案：</strong></p>
                    <ol>
                        <li><strong>RocksDB 状态后端</strong>：
                            <ul>
                                <li>"Flink 内置三种状态后端：<span class="term" data-pronunciation="梅莫瑞">Memory<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="F-S">FS<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="若克斯-D-B">RocksDB<span class="pronun-icon">🔊</span></span>"</li>
                                <li>"TB 级状态肯定用 RocksDB，因为它是嵌入式 KV 数据库，数据存磁盘，内存只缓存热点"</li>
                            </ul>
                        </li>
                        <li><strong>增量 Checkpoint</strong>：
                            <ul>
                                <li>"全量 Checkpoint 太慢（TB 级要 20+ 分钟），用增量 Checkpoint 只保存变化的数据"</li>
                                <li>"类似 Git，每次只提交 diff，不是整个项目"</li>
                            </ul>
                        </li>
                        <li><strong>状态 TTL（Time-To-Live）</strong>：
                            <ul>
                                <li>"不是所有状态都要永久保留，比如只需要 24 小时窗口的数据"</li>
                                <li>"配置 TTL 自动清理过期状态，防止状态无限增长"</li>
                            </ul>
                        </li>
                        <li><strong>磁盘和内存优化</strong>：
                            <ul>
                                <li>"RocksDB 配置：调整 <span class="term" data-pronunciation="布洛克凯希">Block Cache<span class="pronun-icon">🔊</span></span>（内存缓存大小）、<span class="term" data-pronunciation="赖特巴佛">Write Buffer<span class="pronun-icon">🔊</span></span>（写缓冲）"</li>
                                <li>"用 SSD 盘，提升 RocksDB 读写性能"</li>
                            </ul>
                        </li>
                    </ol>
                    
                    <p><strong>效果：</strong>"TB 级状态 Checkpoint 时间控制在 5 分钟内，作业稳定运行，内存占用合理（每个 TaskManager 20-30GB）。"</p>
                </div>

                <div class="answer-box">
                    <h4>如果追问"怎么做容量规划"</h4>
                    <p><strong>什么是容量规划：</strong>"就是评估需要多少资源（CPU、内存、磁盘、网络）才能满足业务需求。"</p>
                    
                    <p><strong>规划步骤：</strong></p>
                    <ol>
                        <li><strong>评估数据量</strong>：
                            <ul>
                                <li>"日均 5000 万笔，每笔 1KB → 每天 50GB 原始数据"</li>
                                <li>"高峰期是平均值的 3 倍 → 峰值每秒 2000-3000 笔"</li>
                            </ul>
                        </li>
                        <li><strong>计算并行度</strong>：
                            <ul>
                                <li>"单个 Task 处理能力 100-200 条/秒"</li>
                                <li>"峰值 3000 条/秒 ÷ 150 条/秒 = 20 并行度"</li>
                                <li>"留 2-3 倍冗余 → 实际配置 64-128 并行度"</li>
                            </ul>
                        </li>
                        <li><strong>内存规划</strong>：
                            <ul>
                                <li>"每个 TaskManager 配置 32GB 内存"</li>
                                <li>"RocksDB 状态后端占 20GB，JVM 堆内存 10GB，剩余系统缓存"</li>
                            </ul>
                        </li>
                        <li><strong>存储规划</strong>：
                            <ul>
                                <li>"Kafka 保留 7 天 → 50GB × 7 = 350GB × 3 副本 = 1TB"</li>
                                <li>"HDFS 存 3 个月 → 50GB × 90 = 4.5TB × 3 副本 = 13TB"</li>
                            </ul>
                        </li>
                    </ol>
                    
                    <p><strong>万能答案：</strong>"我们是根据历史数据和业务预期做规划的。先算峰值，然后留 2-3 倍冗余。实际运行后再根据监控数据调优。关键是要有监控和告警，资源不够提前扩容。"</p>
                </div>

                <div class="warning-box">
                    <h4>⚠️ 避免踩坑的话术</h4>
                    <ul>
                        <li><strong>不要说"我们有几百台服务器"</strong> → 改说"我们有一个中等规模的集群，具体多少台记不太清了"</li>
                        <li><strong>不要说"单表几十亿数据"</strong> → 改说"数据量是亿级的，具体多少亿取决于业务增长"</li>
                        <li><strong>不要说"QPS 达到 10 万"</strong> → 改说"高峰期 QPS 是万级，具体多少要看业务场景"</li>
                        <li><strong>承认局限性</strong>："这个项目规模属于中型，不算特别大，但麻雀虽小五脏俱全，该有的技术挑战都遇到了"</li>
                    </ul>
                </div>

                <div class="example-box">
                    <h4>🎯 完整应对示例</h4>
                    <p><strong>面试官：</strong>"你们日均 5000 万笔，峰值 QPS 多少？"</p>
                    <p><strong>你：</strong>"平均下来每秒几百笔，高峰期能到每秒 2000-3000 笔。不过我们更关注的是持续处理能力，而不是瞬时峰值。"</p>
                    
                    <p><strong>面试官：</strong>"怎么保证处理得过来？"</p>
                    <p><strong>你：</strong>"主要有几个保障机制：一是 Kafka 缓冲削峰，有 7 天保留期；二是 Flink on K8s 弹性扩容；三是做了充分的性能优化，比如 Kafka 分区扩容、Flink 并行度调整、Checkpoint 优化等。实际运行中通过监控 Lag 提前预警，必要时人工扩容。"</p>
                    
                    <p><strong>面试官：</strong>"遇到过数据倾斜吗？"</p>
                    <p><strong>你：</strong>"遇到过。我们按账户 keyBy 时发现某些账户交易特别多，导致那个分区积压。后来改了分区策略，加了二次打散，倾斜基本解决了。"</p>
                    
                    <p><strong>面试官：</strong>"TB 级状态怎么管理？"</p>
                    <p><strong>你：</strong>"用 RocksDB 状态后端，配合增量 Checkpoint。还配了状态 TTL 自动清理过期数据，防止状态无限增长。最后 Checkpoint 时间控制在 5 分钟左右。"</p>
                </div>
            </div>

            <button class="collapsible">⭐ AI 项目重点技术应对</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>面临的问题</h4>
                    <p>简历写了 RAG、向量化、重排序，但你可能对底层原理不够清楚。面试官追问"向量化怎么做的？为什么相似？"</p>
                    
                    <h4>应对策略</h4>
                    <p><strong>不要说"我不太清楚底层原理，只是调用了 <span class="term" data-pronunciation="诶批爱">API<span class="pronun-icon">🔊</span></span>"！</strong></p>
                    <p><strong>正确做法：</strong>用大白话解释原理，举具体例子，体现你理解核心思想（已经在技术详解部分准备好了）。</p>
                </div>

                <div class="answer-box">
                    <h4>向量化怎么说</h4>
                    <p><strong>简单版：</strong>"向量化就是把文本转成一串数字（比如 1024 维向量）。这串数字不是随机的，而是编码了文本的语义信息。相似的文本，向量也会比较相似。"</p>
                    
                    <p><strong>技术版（如果追问）：</strong></p>
                    <ul>
                        <li>"具体是通过 <span class="term" data-pronunciation="传思佛摸">Transformer<span class="pronun-icon">🔊</span></span> 神经网络实现的，我们用的是 <span class="term" data-pronunciation="B-G-E M-3">BGE-M3<span class="pronun-icon">🔊</span></span> 模型"</li>
                        <li>"文本先分词、转<span class="term" data-pronunciation="I-D">ID<span class="pronun-icon">🔊</span></span>，然后经过 12 层 <span class="term" data-pronunciation="传思佛摸">Transformer<span class="pronun-icon">🔊</span></span> 编码，最后输出 1024 维向量"</li>
                        <li>"模型是通过对比学习训练的：相似句子的向量要靠近，不相似的要离远"</li>
                        <li>"我们用余弦相似度衡量向量距离，范围是 -1 到 1，越接近 1 越相似"</li>
                    </ul>
                    
                    <p><strong>举例子：</strong>"比如'如何部署 Flink'和'Flink 怎么安装'，虽然文字不一样，但语义相似，向量的余弦相似度能达到 0.9+。"</p>
                </div>

                <div class="answer-box">
                    <h4>RAG 怎么说</h4>
                    <p><strong>简单版：</strong>"RAG 就是先从知识库检索相关文档，然后把文档和问题一起给大模型，让模型基于真实文档回答。这样可以避免模型瞎编，答案更准确。"</p>
                    
                    <p><strong>技术版（如果追问）：</strong></p>
                    <ul>
                        <li>"文档解析 → 切片（500-1000 字）→ 向量化（BGE-M3）→ 存入 Milvus"</li>
                        <li>"检索时做混合召回：向量检索 Top20 + BM25 关键词检索 Top20"</li>
                        <li>"然后用重排序模型（gte-Qwen2-7B）精排，选出最相关的 3-5 篇"</li>
                        <li>"把这些文档和问题组合成 <span class="term" data-pronunciation="撇破母特">Prompt<span class="pronun-icon">🔊</span></span>，给大模型生成答案"</li>
                    </ul>
                    
                    <p><strong>性能数据：</strong>"检索延迟 50ms 以内，生成答案首字延迟 500ms，端到端 3 秒内返回结果。"</p>
                </div>

                <div class="answer-box">
                    <h4>重排序怎么说</h4>
                    <p><strong>简单版：</strong>"重排序就是二次筛选。第一次检索（粗排）可能召回几十篇文档，但质量参差不齐。重排序模型会精细打分，选出真正相关的 Top3-5。"</p>
                    
                    <p><strong>技术版（如果追问）：</strong></p>
                    <ul>
                        <li>"粗排用的是向量模型（BGE-M3），速度快，但精度一般"</li>
                        <li>"精排用的是 Cross-Encoder 模型（gte-Qwen2-7B），把问题和文档一起编码，打分更准，但速度慢"</li>
                        <li>"所以采用两阶段：粗排召回 30-40 篇 → 精排选出 3-5 篇"</li>
                    </ul>
                    
                    <p><strong>效果对比：</strong>"加了重排序后，Top3 准确率从 70% 提升到 90%+。"</p>
                </div>

                <div class="answer-box">
                    <h4>处理什么数据格式（高频追问）</h4>
                    <p><strong>文档格式：</strong>"支持 PDF、Word、Excel、Markdown、纯文本。用 PDFPlumber 和 Unstructured 库解析，能提取文字、表格、图片。"</p>
                    
                    <p><strong>表格向量化：</strong>"表格先转成 Markdown 格式或 JSON，然后和上下文一起向量化。比如一个销售表格，会转成'2024年Q1销售额100万'这种描述性文字，再向量化。"</p>
                    
                    <p><strong>数据库查询：</strong>"<span class="term" data-pronunciation="特克斯特兔思Q偶">Text2SQL<span class="pronun-icon">🔊</span></span> 输入是用户自然语言，输出是标准 <span class="term" data-pronunciation="思Q偶">SQL<span class="pronun-icon">🔊</span></span>。中间会用到表结构（<span class="term" data-pronunciation="斯key马">Schema<span class="pronun-icon">🔊</span></span>）向量化，找到最相关的表和字段，然后生成 <span class="term" data-pronunciation="思Q偶">SQL<span class="pronun-icon">🔊</span></span>。"</p>
                </div>
            </div>

            <button class="collapsible">⭐ 国产化适配经验应对</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>面临的问题</h4>
                    <p>简历写了"ARM 架构适配、达梦数据库适配、飞腾 CPU、麒麟 OS"，面试官可能追问"具体怎么适配的？遇到什么问题？"</p>
                    
                    <h4>应对策略</h4>
                    <p><strong>不要说"照着文档改就行了"！</strong>这显得工作简单，没有技术含量。</p>
                    <p><strong>正确做法：</strong>说清楚遇到的问题、解决思路、技术难点，体现你的技术能力。</p>
                </div>

                <div class="answer-box">
                    <h4>达梦数据库适配怎么说</h4>
                    <p><strong>背景：</strong>"Hive Metastore 默认只支持 MySQL、PostgreSQL、Oracle，需要适配达梦数据库作为元数据存储。"</p>
                    
                    <p><strong>技术难点：</strong></p>
                    <ul>
                        <li><strong><span class="term explain" data-pronunciation="D-D-L" data-explain="Data Definition Language，数据定义语言，比如CREATE TABLE、ALTER TABLE等建表语句">DDL<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 语法差异</strong>：达梦的 <span class="term" data-pronunciation="哦偷印克瑞门特">AUTO_INCREMENT<span class="pronun-icon">🔊</span></span> 写法和 <span class="term explain" data-pronunciation="迈思Q偶" data-explain="MySQL数据库，开源、免费、常用">MySQL<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 不一样，要改成 <span class="term" data-pronunciation="爱等踢">IDENTITY<span class="pronun-icon">🔊</span></span></li>
                        <li><strong>数据类型映射</strong>：MySQL 的 <span class="term" data-pronunciation="浪特克斯特">LONGTEXT<span class="pronun-icon">🔊</span></span> 要映射成达梦的 <span class="term" data-pronunciation="T-克斯特">TEXT<span class="pronun-icon">🔊</span></span>，<span class="term" data-pronunciation="布林">BOOLEAN<span class="pronun-icon">🔊</span></span> 映射成 <span class="term" data-pronunciation="听踢印特">TINYINT<span class="pronun-icon">🔊</span></span></li>
                        <li><strong>字符集兼容</strong>：达梦默认 <span class="term" data-pronunciation="G-B-K">GBK<span class="pronun-icon">🔊</span></span>，要改成 <span class="term" data-pronunciation="U-T-F-8">UTF-8<span class="pronun-icon">🔊</span></span> 避免中文乱码</li>
                        <li><strong><span class="term" data-pronunciation="拽-K-B-C驱动">JDBC 驱动<span class="pronun-icon">🔊</span></span>替换</strong>：pom.xml 里把 <span class="term" data-pronunciation="迈思Q偶-康乃克特">mysql-connector<span class="pronun-icon">🔊</span></span> 换成 <span class="term" data-pronunciation="达梦8">dm8-jdbc<span class="pronun-icon">🔊</span></span></li>
                    </ul>
                    
                    <p><strong>具体改动：</strong></p>
                    <ul>
                        <li>修改 Hive 源码里的 SQL 建表脚本（几十个表，每个表都要检查）</li>
                        <li>修改 <span class="term" data-pronunciation="达塔牛克理尔思">DataNucleus<span class="pronun-icon">🔊</span></span>（Hive 用的 <span class="term" data-pronunciation="O-R-M">ORM<span class="pronun-icon">🔊</span></span> 框架）配置</li>
                        <li>跑兼容性测试：建库建表、查询、修改分区等</li>
                    </ul>
                    
                    <p><strong>效果：</strong>"适配完成后，Hive Metastore 在达梦上稳定运行，元数据读写性能和 MySQL 差不多。"</p>
                </div>

                <div class="answer-box">
                    <h4>ARM 架构适配怎么说</h4>
                    <p><strong>背景：</strong>"国产服务器用的是飞腾 CPU（ARM 架构），大数据组件大部分是 x86 编译的，需要重新编译。"</p>
                    
                    <p><strong>技术难点：</strong></p>
                    <ul>
                        <li><strong>依赖库缺失</strong>：有些 C/C++ 依赖库没有 ARM 版本，要自己编译或找替代</li>
                        <li><strong>JNI 调用问题</strong>：比如 <span class="term" data-pronunciation="H-迪-F-S">HDFS<span class="pronun-icon">🔊</span></span> 的 <span class="term" data-pronunciation="雷布H-迪-F-S">libhdfs<span class="pronun-icon">🔊</span></span>（C 库），要重新编译 ARM 版本</li>
                        <li><strong>JVM 优化</strong>：ARM 的 <span class="term" data-pronunciation="加-V-M">JVM<span class="pronun-icon">🔊</span></span> 性能和 x86 有差异，要调优 <span class="term" data-pronunciation="鸡-C参数">GC 参数<span class="pronun-icon">🔊</span></span></li>
                        <li><strong>ClickHouse 特殊处理</strong>：源码里有汇编代码，要改成 ARM 指令集，还要修改 <span class="term" data-pronunciation="C-梅克">CMake<span class="pronun-icon">🔊</span></span> 编译配置</li>
                    </ul>
                    
                    <p><strong>具体工作：</strong></p>
                    <ul>
                        <li>搭建 ARM 编译环境（飞腾服务器 + 国产 OS）</li>
                        <li>逐个组件尝试编译，解决依赖问题</li>
                        <li>编译成功后做功能测试和性能测试</li>
                        <li>编写部署文档和运维手册</li>
                    </ul>
                    
                    <p><strong>效果：</strong>"适配完成后，所有组件在 ARM 上稳定运行，性能略低于 x86（约 10-15%），但满足业务需求。"</p>
                </div>

                <div class="answer-box">
                    <h4>如果追问"为什么要国产化"</h4>
                    <p><strong>标准答案：</strong>"主要是满足国家信创政策要求，特别是金融、政府等关键领域。国产化包括 CPU（飞腾、鲲鹏）、操作系统（麒麟、统信）、数据库（达梦、人大金仓），确保核心技术自主可控。"</p>
                    <p><strong>补充：</strong>"我们项目是给银行做的，监管要求必须用国产化方案，所以全栈都要适配。"</p>
                </div>
            </div>

            <button class="collapsible">⭐ 昇腾 910B 显卡适配应对</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>面临的问题</h4>
                    <p>简历写了"昇腾 910B 显卡适配"，这是国产 AI 芯片，不是英伟达 GPU。面试官可能追问"为什么用昇腾？怎么适配的？"</p>
                    
                    <h4>应对策略</h4>
                    <p><strong>不要说"就是换了显卡驱动"！</strong>适配工作很复杂，要体现你的技术深度。</p>
                </div>

                <div class="answer-box">
                    <h4>为什么用昇腾 910B</h4>
                    <p><strong>背景：</strong>"军工企业项目，受美国出口管制影响，买不到高端英伟达 GPU（A100/H100）。昇腾 910B 是国产替代方案。"</p>
                    <p><strong>硬件规格：</strong></p>
                    <ul>
                        <li>单卡 64GB <span class="term" data-pronunciation="H-B-M-2">HBM2<span class="pronun-icon">🔊</span></span> 显存（比 A100 80GB 略小）</li>
                        <li>典型配置：8 卡服务器（总显存 512GB）</li>
                        <li>算力：比 A100 低 30-40%，但够用</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>适配工作怎么说</h4>
                    <p><strong>核心问题：</strong>"<span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span>、<span class="term explain" data-pronunciation="炮奇" data-explain="深度学习框架">PyTorch<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 默认只支持 <span class="term" data-pronunciation="恩维迪亚">NVIDIA<span class="pronun-icon">🔊</span></span> <span class="term explain" data-pronunciation="酷达" data-explain="NVIDIA显卡的编程接口">CUDA<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span>，昇腾用的是华为自己的 <span class="term explain" data-pronunciation="C-A-N-N" data-explain="Compute Architecture for Neural Networks，华为昇腾AI计算架构。

✦ 是什么：
• 华为的CUDA替代品
• 支持昇腾AI处理器（910/310系列）
• 提供算子库、编译器、调试工具
• 类比：CANN之于昇腾 = CUDA之于NVIDIA

✦ 为什么要适配：
• 国产化要求（军工、金融等行业）
• 不依赖美国技术（规避制裁风险）
• 昇腾910B性价比高（同性能比A100便宜）

✦ 适配难点：
• 算子兼容：PyTorch有几千个算子，CANN未必都有
• 性能差异：需要针对昇腾架构优化
• 生态不完善：文档少、社区小
• 调试困难：报错信息不如CUDA清晰

✦ 项目中的做法：
• 替换CUDA调用→CANN算子
• 优化Attention、MatMul等关键算子
• 调整显存分配策略
• 实测性能达到A100的90%+">CANN<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 框架，需要适配。"</p>
                    
                    <p><strong>适配层级：</strong></p>
                    <ol>
                        <li><strong>算子库替换</strong>：把 <span class="term" data-pronunciation="酷达">CUDA<span class="pronun-icon">🔊</span></span> 算子（<span class="term" data-pronunciation="阿腾神">Attention<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="妈绰莫">MatMul<span class="pronun-icon">🔊</span></span>、<span class="term" data-pronunciation="雷尔诺姆">LayerNorm<span class="pronun-icon">🔊</span></span> 等）映射到 CANN 算子</li>
                        <li><strong>显存管理</strong>：CANN 的显存分配逻辑和 CUDA 不一样，要调整 vLLM 的 <span class="term explain" data-pronunciation="佩之的腾神" data-explain="PagedAttention是vLLM的核心技术，把Attention的KV Cache分页管理，像操作系统的虚拟内存">PagedAttention<span class="pronun-icon">🔊</span><span class="explain-icon">❓</span></span> 显存管理</li>
                        <li><strong>算子优化</strong>：有些算子 CANN 原生不支持或性能差，要手写 <span class="term" data-pronunciation="酷特">CANN Kernel<span class="pronun-icon">🔊</span></span> 优化</li>
                        <li><strong>兼容性测试</strong>：测试不同模型（Qwen、DeepSeek、ChatGLM）在昇腾上的推理效果</li>
                    </ol>
                    
                    <p><strong>具体工作：</strong></p>
                    <ul>
                        <li>安装 CANN 开发套件和驱动</li>
                        <li>使用华为提供的 <span class="term" data-pronunciation="炮奇">PyTorch<span class="pronun-icon">🔊</span></span> 昇腾版（基于 PyTorch 1.11 适配）</li>
                        <li>修改 <span class="term" data-pronunciation="v-艾偶-艾偶-艾姆">vLLM<span class="pronun-icon">🔊</span></span> 源码，适配昇腾的算子 <span class="term" data-pronunciation="诶批爱">API<span class="pronun-icon">🔊</span></span></li>
                        <li>性能调优：调整 batch size、sequence length、KV cache 大小等</li>
                        <li>验证准确性：对比昇腾和 GPU 的输出结果，确保一致</li>
                    </ul>
                    
                    <p><strong>效果：</strong>"适配后 Qwen2.5-14B 模型在 8 卡昇腾上推理速度约为 A100 的 70%，首字延迟 500ms 左右，满足生产要求。"</p>
                </div>

                <div class="answer-box">
                    <h4>遇到的主要问题</h4>
                    <ul>
                        <li><strong>算子性能差异</strong>：某些算子（如 FlashAttention）昇腾没有优化版本，推理速度慢 → <strong>解决：</strong>降级到标准 Attention 或手写优化算子</li>
                        <li><strong>显存溢出</strong>：CANN 显存管理和 CUDA 不同，容易 OOM → <strong>解决：</strong>减小 batch size，调整 KV cache 策略</li>
                        <li><strong>精度损失</strong>：量化模型（INT8）在昇腾上精度下降 → <strong>解决：</strong>改用 FP16，或调整量化参数</li>
                    </ul>
                </div>
            </div>

            <button class="collapsible">⭐ 技术选型原因应对</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>面临的问题</h4>
                    <p>面试官可能追问"为什么用 Flink 不用 Spark？为什么用 Hudi 不用 Iceberg？为什么用 vLLM 不用 TensorRT？"</p>
                    
                    <h4>应对策略</h4>
                    <p><strong>不要说"领导定的"或"别人推荐的"！</strong>这显得你没有技术判断力。</p>
                    <p><strong>正确做法：</strong>说出技术选型的理由，对比优劣，体现你的技术视野。</p>
                </div>

                <div class="answer-box">
                    <h4>⭐ Flink vs Spark Streaming 全面对比</h4>
                    <table>
                        <tr>
                            <th>对比维度</th>
                            <th>Flink</th>
                            <th>Spark Streaming</th>
                        </tr>
                        <tr>
                            <td><strong>处理模型</strong></td>
                            <td>⭐真流处理（事件驱动）<br>一条一条处理</td>
                            <td>微批处理（Micro-Batch）<br>积累一批再处理</td>
                        </tr>
                        <tr>
                            <td><strong>延迟</strong></td>
                            <td>⭐毫秒级（10-100ms）</td>
                            <td>秒级（1-5s）</td>
                        </tr>
                        <tr>
                            <td><strong>吞吐量</strong></td>
                            <td>⭐⭐⭐⭐<br>单机百万QPS</td>
                            <td>⭐⭐⭐⭐⭐<br>批处理更强</td>
                        </tr>
                        <tr>
                            <td><strong>状态管理</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>• TB级状态（RocksDB）<br>• Checkpoint机制成熟<br>• Exactly-Once</td>
                            <td>⭐⭐⭐<br>• 状态支持较弱<br>• 内存状态为主</td>
                        </tr>
                        <tr>
                            <td><strong>反压机制</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>自动反压（下游慢自动降速）</td>
                            <td>⭐⭐⭐<br>需手动调优</td>
                        </tr>
                        <tr>
                            <td><strong>CDC支持</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>Flink CDC官方支持<br>Oracle/MySQL/DB2</td>
                            <td>⭐⭐<br>需自己集成Debezium</td>
                        </tr>
                        <tr>
                            <td><strong>流批一体</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>同一套API处理流和批</td>
                            <td>⭐⭐⭐<br>Spark SQL可以，但不如Flink</td>
                        </tr>
                        <tr>
                            <td><strong>Window窗口</strong></td>
                            <td>⭐⭐⭐⭐⭐<br>丰富（滑动、滚动、会话）<br>Watermark机制</td>
                            <td>⭐⭐⭐<br>窗口支持较少</td>
                        </tr>
                        <tr>
                            <td><strong>生态</strong></td>
                            <td>⭐⭐⭐⭐<br>流处理领域主流</td>
                            <td>⭐⭐⭐⭐⭐<br>大数据生态最完善<br>机器学习MLlib强</td>
                        </tr>
                        <tr>
                            <td><strong>学习曲线</strong></td>
                            <td>⭐⭐⭐<br>概念多（Watermark、State）</td>
                            <td>⭐⭐⭐⭐<br>有Spark基础易上手</td>
                        </tr>
                        <tr>
                            <td><strong>资源消耗</strong></td>
                            <td>⭐⭐⭐⭐<br>内存占用适中</td>
                            <td>⭐⭐⭐<br>内存消耗大</td>
                        </tr>
                        <tr>
                            <td><strong>适用场景</strong></td>
                            <td>• 实时性要求高⭐<br>• 大状态场景<br>• CDC数据同步<br>• 复杂事件处理</td>
                            <td>• 批流统一<br>• 机器学习<br>• 对延迟不敏感<br>• 已有Spark生态</td>
                        </tr>
                    </table>
                    
                    <p><strong>面试话术：</strong></p>
                    <p>"我们银行项目有三个核心需求：</p>
                    <ol>
                        <li><strong>低延迟</strong>：交易数据要秒级入湖，Flink毫秒级延迟，Spark秒级不够</li>
                        <li><strong>大状态</strong>：需要维护账户累计金额等状态，Flink的RocksDB可以支持TB级状态</li>
                        <li><strong>CDC同步</strong>：从Oracle实时同步数据，Flink CDC生态成熟，开箱即用</li>
                    </ol>
                    <p>综合考虑，Flink更适合。如果是离线批处理或机器学习，我会选Spark。"</p>
                </div>

                <div class="answer-box">
                    <h4>为什么用 Hudi 而不是 Iceberg</h4>
                    <p><strong>原因：</strong></p>
                    <ul>
                        <li><strong>Flink 集成度好</strong>：Hudi 和 Flink 集成更成熟，官方维护；Iceberg 当时（2021-2022）Flink 支持还不完善</li>
                        <li><strong>Upsert 性能好</strong>：Hudi 的 MOR 表对高频更新（CDC）优化更好</li>
                        <li><strong>社区活跃</strong>：Hudi 是 Apache 顶级项目，文档和案例丰富</li>
                    </ul>
                    <p><strong>劣势承认：</strong>"Iceberg 这两年发展很快，生态更好，如果重新选可能会考虑 Iceberg。"</p>
                </div>

                <div class="answer-box">
                    <h4>为什么用 vLLM 而不是 TensorRT-LLM</h4>
                    <p><strong>原因：</strong></p>
                    <ul>
                        <li><strong>易用性</strong>：vLLM 开箱即用，兼容 HuggingFace 模型；TensorRT-LLM 需要模型转换，配置复杂</li>
                        <li><strong>兼容性</strong>：vLLM 支持各种开源模型（Qwen、DeepSeek、ChatGLM），TensorRT-LLM 支持有限</li>
                        <li><strong>PagedAttention</strong>：vLLM 的核心技术，显存利用率高，吞吐量大</li>
                    </ul>
                    <p><strong>劣势承认：</strong>"TensorRT-LLM 性能更极致（快 20-30%），但工程代价高。我们优先考虑快速迭代，所以选 vLLM。"</p>
                </div>

                <div class="answer-box">
                    <h4>为什么用 Milvus 而不是 ES（Elasticsearch）</h4>
                    <p><strong>原因：</strong></p>
                    <ul>
                        <li><strong>专门向量数据库</strong>：Milvus 专为向量检索优化，HNSW 索引速度快</li>
                        <li><strong>高维向量支持好</strong>：支持 1024 维甚至更高维度向量；ES 向量功能是后加的，性能一般</li>
                        <li><strong>水平扩展</strong>：Milvus 原生支持分布式，百万级文档也能毫秒级检索</li>
                    </ul>
                    <p><strong>何时用 ES：</strong>"如果以关键词检索为主，只是辅助用向量，那 ES 够用了。我们是向量检索为主，所以选 Milvus。"</p>
                </div>
            </div>

            <button class="collapsible">⭐ 项目难点和问题应对</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>面临的问题</h4>
                    <p>面试官经常问"项目遇到最大的困难是什么？怎么解决的？"这是考察你的问题解决能力。</p>
                    
                    <h4>应对策略</h4>
                    <p><strong>不要说"没遇到什么问题"！</strong>这显得项目太简单或你没参与核心工作。</p>
                    <p><strong>正确做法：</strong>准备 2-3 个有代表性的问题，说清楚"问题现象 → 原因分析 → 解决方案 → 效果"。</p>
                </div>

                <div class="answer-box">
                    <h4>问题1：Flink Checkpoint 频繁失败</h4>
                    <p><strong>现象：</strong>"银行项目上线初期，Flink 作业 Checkpoint 成功率只有 70%，经常因为 Checkpoint 超时导致作业重启，影响业务。"</p>
                    
                    <p><strong>原因分析：</strong></p>
                    <ul>
                        <li>TB 级状态，全量 Checkpoint 需要 20+ 分钟</li>
                        <li>超时时间设置太短（10 分钟）</li>
                        <li>网络抖动导致 Checkpoint 写 HDFS 慢</li>
                    </ul>
                    
                    <p><strong>解决方案：</strong></p>
                    <ul>
                        <li><strong>启用 RocksDB 增量 Checkpoint</strong>：只保存变化的数据，时间从 20 分钟降到 5 分钟</li>
                        <li><strong>调整超时参数</strong>：从 10 分钟调到 15 分钟，容忍偶发慢查询</li>
                        <li><strong>配置失败容忍</strong>：允许连续失败 3 次再重启，避免偶发故障导致频繁重启</li>
                        <li><strong>优化 HDFS 写入</strong>：增加 DataNode，优化网络带宽</li>
                    </ul>
                    
                    <p><strong>效果：</strong>"Checkpoint 成功率从 70% 提升到 99%+，作业稳定性大幅提升。"</p>
                </div>

                <div class="answer-box">
                    <h4>问题2：Kafka 消费延迟严重</h4>
                    <p><strong>现象：</strong>"高峰期 Kafka Lag 积压到几百万条，数据处理延迟从秒级涨到几十分钟。"</p>
                    
                    <p><strong>原因分析：</strong></p>
                    <ul>
                        <li>Kafka 只有 64 个分区，Flink 并行度最多 64</li>
                        <li>服务器有 128 核 CPU，但并行度只有 64，CPU 利用率不足</li>
                        <li>下游写 Oracle 是同步阻塞的，成为瓶颈</li>
                    </ul>
                    
                    <p><strong>解决方案：</strong></p>
                    <ul>
                        <li><strong>Kafka 分区扩容</strong>：从 64 扩到 256（需要重新分配数据，选在凌晨低峰期操作）</li>
                        <li><strong>Flink 并行度提升</strong>：从 64 调到 128，充分利用 CPU</li>
                        <li><strong>异步 IO</strong>：改造 Oracle Sink 为异步批量写入（攒够 100 条或 5 秒超时统一提交）</li>
                    </ul>
                    
                    <p><strong>效果：</strong>"吞吐量提升 3 倍，高峰期 Lag 控制在 5 分钟以内，处理延迟降到秒级。"</p>
                </div>

                <div class="answer-box">
                    <h4>问题3：RAG 召回效果差</h4>
                    <p><strong>现象：</strong>"AI 问答项目初期，用户问问题经常答非所问，召回的文档不相关。"</p>
                    
                    <p><strong>原因分析：</strong></p>
                    <ul>
                        <li>文档切片太大（2000 字），包含太多无关信息</li>
                        <li>只用向量检索，关键词匹配不好（比如专有名词、缩写）</li>
                        <li>没有重排序，粗排质量不高</li>
                    </ul>
                    
                    <p><strong>解决方案：</strong></p>
                    <ul>
                        <li><strong>优化切片策略</strong>：从 2000 字改为 500-1000 字，按段落语义切分</li>
                        <li><strong>混合检索</strong>：向量检索 + BM25 关键词检索，召回更全面</li>
                        <li><strong>引入重排序</strong>：用 gte-Qwen2-7B 二次精排，Top3 准确率从 70% 提升到 90%+</li>
                        <li><strong>Query 改写</strong>：用大模型改写用户问题，生成多个相似问法，提高召回率</li>
                    </ul>
                    
                    <p><strong>效果：</strong>"用户满意度从 60% 提升到 85%，答非所问的情况大幅减少。"</p>
                </div>
            </div>

            <button class="collapsible">⭐ 跨部门协作应对</button>
            <div class="collapsible-content">
                <div class="warning-box">
                    <h4>为什么要准备</h4>
                    <p>面试官经常问"和业务部门怎么配合？需求不明确怎么办？"这是考察你的沟通协调能力。</p>
                </div>

                <div class="answer-box">
                    <h4>需求不明确怎么办</h4>
                    <p><strong>场景：</strong>"业务方说'我要实时数据'，但没说要多实时、看哪些数据、怎么展示。"</p>
                    
                    <p><strong>应对方法：</strong></p>
                    <ol>
                        <li><strong>反向提问</strong>："实时是指秒级还是分钟级？延迟 5 分钟能接受吗？"</li>
                        <li><strong>举例确认</strong>："您是想看今天截至现在的交易总额，还是最近 1 小时的交易趋势？"</li>
                        <li><strong>给出方案对比</strong>："秒级实时成本高，需要 Flink；分钟级可以用定时任务，成本低。您选哪个？"</li>
                        <li><strong>MVP 快速验证</strong>："我们先做个原型，您看看是不是这个效果，再深入开发。"</li>
                    </ol>
                </div>

                <div class="answer-box">
                    <h4>技术方案如何给业务讲清楚</h4>
                    <p><strong>错误做法：</strong>"我们用 Flink 实时消费 Kafka，通过 RocksDB 状态后端..."（业务听不懂）</p>
                    
                    <p><strong>正确做法：</strong></p>
                    <ul>
                        <li><strong>用业务语言</strong>："数据产生后 3 秒内就能在您的报表里看到，不用等到第二天。"</li>
                        <li><strong>类比说明</strong>："就像外卖 App，您下单了马上能看到骑手接单，不用等一天才通知您。"</li>
                        <li><strong>强调价值</strong>："实时监控可以提前发现风险，以前要第二天才知道，现在 3 秒就告警。"</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>遇到不合理需求怎么办</h4>
                    <p><strong>场景：</strong>"业务要求'1 秒内查询 10 年的全量交易明细'，技术上做不到或成本极高。"</p>
                    
                    <p><strong>应对方法：</strong></p>
                    <ol>
                        <li><strong>了解真实需求</strong>："您具体要解决什么问题？是做对账、还是查异常、还是做统计？"</li>
                        <li><strong>提出替代方案</strong>：
                            <ul>
                                <li>"查询明细：按时间范围查，比如查某一天或某个月，1 秒返回"</li>
                                <li>"做统计：我们提前算好汇总数据，查统计 1 秒返回"</li>
                            </ul>
                        </li>
                        <li><strong>量化成本</strong>："10 年全量明细 1 秒返回，需要几千万内存和几十台服务器，成本上百万。按月查询，现有资源就够。"</li>
                        <li><strong>寻求妥协</strong>："日常查询 99% 都是查近期数据，我们优化近期查询速度，历史数据允许慢一点，您看行吗？"</li>
                    </ol>
                </div>
            </div>

            <button class="collapsible">通用面试技巧</button>
            <div class="collapsible-content">
                <div class="highlight-box">
                    <h4>遇到不会的问题怎么办</h4>
                    <ul>
                        <li><strong>千万别说"不知道"</strong>：显得你不动脑子</li>
                        <li><strong>转移视角</strong>："这个细节我没深入研究，但我知道它的核心思路是..."（说你知道的部分）</li>
                        <li><strong>关联已知</strong>："这个我没用过，但我用过类似的 XX，原理应该差不多"</li>
                        <li><strong>主动学习</strong>："这块我确实不太熟，您能简单介绍一下吗？我很感兴趣"（反客为主）</li>
                    </ul>
                </div>

                <div class="example-box">
                    <h4>扬长避短策略</h4>
                    <ul>
                        <li><strong>主动引导话题</strong>：面试官问的问题不擅长，简短回答后，主动引到你擅长的话题</li>
                        <li><strong>举具体例子</strong>：不要空谈概念，举你项目里的实际例子，显得有经验</li>
                        <li><strong>强调成果</strong>：性能提升 X 倍、准确率 X%、解决了 XX 问题，用数据说话</li>
                    </ul>
                </div>

                <div class="warning-box">
                    <h4>红线（千万别碰）</h4>
                    <ul>
                        <li>❌ 说"我只负责 XX，其他不了解"（体现不出全局观）</li>
                        <li>❌ 对简历内容说"记不清了"（说明你简历有水分）</li>
                        <li>❌ 数据说得太精确又前后矛盾（容易穿帮）</li>
                        <li>❌ 吹牛说没做过的事（很容易被追问露馅）</li>
                    </ul>
                </div>

                <div class="answer-box">
                    <h4>黄金法则</h4>
                    <ol>
                        <li><strong>说你确实做过的</strong>：哪怕只是一小部分，深挖细节</li>
                        <li><strong>用技术视角说业务</strong>：业务不清楚没关系，强调技术实现</li>
                        <li><strong>数据说范围不说精确值</strong>："几千万级"比"5347 万"安全</li>
                        <li><strong>优化说思路不背参数</strong>：说"Kafka 分区扩容提升吞吐"，不要说"broker.num.partitions=256"</li>
                        <li><strong>保持自信从容</strong>：就算不会也别慌，转移话题或者请教学习</li>
                    </ol>
                </div>
            </div>
        </div>
    </div>

    <button class="back-to-top" onclick="scrollToTop()">↑</button>
    <button class="quick-nav-toggle" onclick="toggleQuickNav()">☰</button>
    
    <!-- 快速导航面板 -->
    <div class="nav-overlay" onclick="toggleQuickNav()"></div>
    <div class="quick-nav-panel">
        <div class="quick-nav-header">
            <h3>📑 快速导航</h3>
            <button class="quick-nav-close" onclick="toggleQuickNav()">✕</button>
        </div>
        
        <div class="quick-nav-section">
            <h4>AI 大模型技术</h4>
            <ul class="quick-nav-items">
                <li><a href="#" onclick="navigateTo(1, 0)" class="important">华为昇腾 910B</a></li>
                <li><a href="#" onclick="navigateTo(1, 1)" class="important">RAG + 向量化</a></li>
                <li><a href="#" onclick="navigateTo(1, 2)">Text2SQL</a></li>
                <li><a href="#" onclick="navigateTo(1, 3)">vLLM 推理引擎</a></li>
                <li><a href="#" onclick="navigateTo(1, 4)">Triton 推理服务器</a></li>
                <li><a href="#" onclick="navigateTo(1, 5)">向量检索与重排序</a></li>
                <li><a href="#" onclick="navigateTo(1, 6)">大模型系列</a></li>
                <li><a href="#" onclick="navigateTo(1, 7)">模型优化技术</a></li>
            </ul>
        </div>
        
        <div class="quick-nav-section">
            <h4>大数据技术</h4>
            <ul class="quick-nav-items">
                <li><a href="#" onclick="navigateTo(2, 0)" class="important">Flink 实时计算</a></li>
                <li><a href="#" onclick="navigateTo(2, 1)">Kafka 消息队列</a></li>
                <li><a href="#" onclick="navigateTo(2, 2)">Hudi 数据湖</a></li>
                <li><a href="#" onclick="navigateTo(2, 3)">Flink CDC</a></li>
                <li><a href="#" onclick="navigateTo(2, 4)" class="important">反压（Backpressure）</a></li>
                <li><a href="#" onclick="navigateTo(2, 5)">K8s 容器编排</a></li>
                <li><a href="#" onclick="navigateTo(2, 6)">Java 技术栈</a></li>
                <li><a href="#" onclick="navigateTo(2, 7)">数据库</a></li>
            </ul>
        </div>
        
        <div class="quick-nav-section">
            <h4>项目介绍</h4>
            <ul class="quick-nav-items">
                <li><a href="#" onclick="navigateTo(3, 0)" class="important">SuperAgent 智能体平台</a></li>
                <li><a href="#" onclick="navigateTo(3, 1)" class="important">企业智能问答平台</a></li>
                <li><a href="#" onclick="navigateTo(3, 2)" class="important">银行实时数据湖</a></li>
                <li><a href="#" onclick="navigateTo(3, 3)">自研大数据平台</a></li>
                <li><a href="#" onclick="navigateTo(3, 4)">HDP 二次开发</a></li>
                <li><a href="#" onclick="navigateTo(3, 5)">舆情分析系统</a></li>
            </ul>
        </div>
        
        <div class="quick-nav-section">
            <h4>面试应对策略</h4>
            <ul class="quick-nav-items">
                <li><a href="#" onclick="navigateTo(4, 0)" class="important">银行业务场景</a></li>
                <li><a href="#" onclick="navigateTo(4, 1)" class="important">大数据量处理</a></li>
                <li><a href="#" onclick="navigateTo(4, 2)" class="important">AI 技术应对</a></li>
                <li><a href="#" onclick="navigateTo(4, 3)">国产化适配</a></li>
                <li><a href="#" onclick="navigateTo(4, 4)">昇腾 910B 适配</a></li>
                <li><a href="#" onclick="navigateTo(4, 5)">技术选型原因</a></li>
                <li><a href="#" onclick="navigateTo(4, 6)">项目难点问题</a></li>
                <li><a href="#" onclick="navigateTo(4, 7)">跨部门协作</a></li>
                <li><a href="#" onclick="navigateTo(4, 8)">通用面试技巧</a></li>
            </ul>
        </div>
    </div>

    <script>
        // 快速导航控制
        function toggleQuickNav() {
            const panel = document.querySelector('.quick-nav-panel');
            const overlay = document.querySelector('.nav-overlay');
            panel.classList.toggle('active');
            overlay.classList.toggle('active');
        }
        
        // 导航到指定位置
        function navigateTo(tabIndex, sectionIndex) {
            // 切换到对应标签页
            switchTab(tabIndex);
            
            // 关闭导航面板
            toggleQuickNav();
            
            // 滚动到对应section
            setTimeout(() => {
                const content = document.querySelectorAll('.content')[tabIndex];
                const buttons = content.querySelectorAll('.collapsible');
                if (buttons[sectionIndex]) {
                    // 展开对应的collapsible
                    if (!buttons[sectionIndex].classList.contains('active')) {
                        buttons[sectionIndex].click();
                    }
                    // 滚动到该位置
                    buttons[sectionIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
                } else {
                    // 如果没有collapsible，直接滚动到顶部
                    content.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }
            }, 100);
        }
        
        // 标签页切换
        function switchTab(index) {
            const tabs = document.querySelectorAll('.nav-tab');
            const contents = document.querySelectorAll('.content');
            
            tabs.forEach(tab => tab.classList.remove('active'));
            contents.forEach(content => content.classList.remove('active'));
            
            tabs[index].classList.add('active');
            contents[index].classList.add('active');

            // 保存当前标签页
            localStorage.setItem('activeTab', index);

            window.scrollTo(0, 0);
        }

        // 页面加载时恢复上次的标签页
        window.addEventListener('DOMContentLoaded', function() {
            const savedTab = localStorage.getItem('activeTab');
            if (savedTab !== null) {
                switchTab(parseInt(savedTab));
            }
        });

        // 折叠展开功能
        document.addEventListener('DOMContentLoaded', function() {
            const collapsibles = document.querySelectorAll('.collapsible');
            
            collapsibles.forEach(function(collapsible) {
                collapsible.addEventListener('click', function() {
                    this.classList.toggle('active');
                    const content = this.nextElementSibling;
                    
                    if (content.classList.contains('active')) {
                        content.classList.remove('active');
                        content.style.maxHeight = null;
                    } else {
                        content.classList.add('active');
                        content.style.maxHeight = content.scrollHeight + "px";
                    }
                });
            });
        });

        // 谐音和术语解释点击切换（手机端）
        document.addEventListener('DOMContentLoaded', function() {
            // 检测是否为触摸设备
            const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
            
            if (isTouchDevice) {
                document.addEventListener('click', function(e) {
                    const target = e.target.closest('.term, .explain');
                    
                    if (target) {
                        // 判断是否同时有term和explain类
                        const hasBoth = target.classList.contains('term') && target.classList.contains('explain');
                        
                        // 移除其他所有的显示状态
                        document.querySelectorAll('.term.show-pronunciation, .explain.show-explain, .term.explain.show-combined').forEach(t => {
                            if (t !== target) {
                                t.classList.remove('show-pronunciation');
                                t.classList.remove('show-explain');
                                t.classList.remove('show-combined');
                            }
                        });
                        
                        if (hasBoth) {
                            // 同时显示谐音和解释（使用合并tooltip）
                            target.classList.toggle('show-combined');
                        } else if (target.classList.contains('term')) {
                            // 只显示谐音
                            target.classList.toggle('show-pronunciation');
                        } else if (target.classList.contains('explain')) {
                            // 只显示解释
                            target.classList.toggle('show-explain');
                        }
                        
                        e.stopPropagation();
                    } else {
                        // 点击其他地方隐藏所有提示
                        document.querySelectorAll('.term.show-pronunciation').forEach(t => {
                            t.classList.remove('show-pronunciation');
                        });
                        document.querySelectorAll('.explain.show-explain').forEach(t => {
                            t.classList.remove('show-explain');
                        });
                        document.querySelectorAll('.term.explain.show-combined').forEach(t => {
                            t.classList.remove('show-combined');
                        });
                    }
                });
            }
        });

        // 返回顶部
        function scrollToTop() {
            window.scrollTo({
                top: 0,
                behavior: 'smooth'
            });
        }

        // 显示/隐藏返回顶部按钮
        window.onscroll = function() {
            const backToTop = document.querySelector('.back-to-top');
            if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 300) {
                backToTop.style.display = 'block';
            } else {
                backToTop.style.display = 'none';
            }
        };

        // 搜索功能
        // ==================== 增强搜索功能 ====================
        let currentSearchIndex = 0;
        let searchMatches = [];
        let searchResultsData = [];  // 搜索结果数据（用于侧边栏显示）
        
        const searchInput = document.getElementById('searchInput');
        const searchClear = document.getElementById('searchClear');
        
        // 侧边栏搜索相关元素
        const searchTogglePanel = document.getElementById('searchTogglePanel');
        const searchResultsPanel = document.getElementById('searchResultsPanel');
        const searchResultsList = document.getElementById('searchResultsList');
        const searchResultsClose = document.querySelector('.search-results-close');
        const searchResultsTitle = document.getElementById('searchResultsTitle');
        const sidebarPrevBtn = document.getElementById('sidebarPrevBtn');
        const sidebarNextBtn = document.getElementById('sidebarNextBtn');
        const sidebarCounter = document.getElementById('sidebarCounter');
        
        // 搜索输入事件
        searchInput.addEventListener('input', function(e) {
            const searchTerm = e.target.value.trim();
            
            // 显示/隐藏清空按钮
            if (searchTerm) {
                searchClear.classList.add('visible');
            } else {
                searchClear.classList.remove('visible');
            }
            
            performSearch(searchTerm);
        });
        
        // Enter键导航
        searchInput.addEventListener('keydown', function(e) {
            if (e.key === 'Enter') {
                e.preventDefault();
                if (e.shiftKey) {
                    navigateSearch('prev');
                } else {
                    navigateSearch('next');
                }
            }
        });
        
        // 清空按钮
        searchClear.addEventListener('click', function() {
            searchInput.value = '';
            searchClear.classList.remove('visible');
            performSearch('');
        });
        
        // 执行搜索
        function performSearch(searchTerm) {
            const activeContent = document.querySelector('.content.active');
            currentSearchIndex = 0;
            searchMatches = [];
            searchResultsData = [];
            
            // 清除所有高亮
            clearHighlights(activeContent);
            
            if (searchTerm === '') {
                updateSearchUI(0, 0);
                renderSearchResults();
                const panel = document.getElementById('searchResultsPanel');
                if (panel) panel.classList.remove('show');
                return;
            }
            
            // 搜索前先展开所有折叠内容
            const collapsibleContents = activeContent.querySelectorAll('.collapsible-content');
            collapsibleContents.forEach(content => {
                if (content.style.maxHeight === '' || content.style.maxHeight === '0px') {
                    // 临时展开以便搜索
                    content.style.maxHeight = content.scrollHeight + 'px';
                    content.setAttribute('data-temp-expanded', 'true');
                }
            });
            
            // 高亮搜索结果
            highlightText(activeContent, searchTerm.toLowerCase());
            
            // 收集所有高亮的mark元素
            searchMatches = Array.from(activeContent.querySelectorAll('mark'));
            
            // 生成搜索结果数据
            searchMatches.forEach((mark, index) => {
                searchResultsData.push({
                    index: index,
                    element: mark,
                    section: getSectionName(mark),
                    subsection: getSubsectionName(mark),
                    context: extractContext(mark)
                });
            });
            
            // 渲染搜索结果到侧边栏
            renderSearchResults();
            
            // 如果有结果，自动打开侧边栏并定位到第一个
            if (searchMatches.length > 0) {
                currentSearchIndex = 0;
                highlightCurrent();
                scrollToCurrentMatch();
                const panel = document.getElementById('searchResultsPanel');
                if (panel) panel.classList.add('show');
            } else {
                // 如果没有结果，恢复折叠状态
                collapsibleContents.forEach(content => {
                    if (content.getAttribute('data-temp-expanded') === 'true') {
                        content.style.maxHeight = '0px';
                        content.removeAttribute('data-temp-expanded');
                    }
                });
            }
            
            updateSearchUI(searchMatches.length, currentSearchIndex + 1);
        }
        
        // 导航搜索结果
        function navigateSearch(direction) {
            if (searchMatches.length === 0) return;
            
            // 移除当前高亮
            if (searchMatches[currentSearchIndex]) {
                searchMatches[currentSearchIndex].classList.remove('current');
            }
            
            // 更新索引
            if (direction === 'next') {
                currentSearchIndex = (currentSearchIndex + 1) % searchMatches.length;
            } else {
                currentSearchIndex = (currentSearchIndex - 1 + searchMatches.length) % searchMatches.length;
            }
            
            // 高亮当前结果
            highlightCurrent();
            scrollToCurrentMatch();
            updateSearchUI(searchMatches.length, currentSearchIndex + 1);
            
            // 同步更新侧边栏
            updateSidebarActiveItem(currentSearchIndex);
        }
        
        // 更新侧边栏当前项
        function updateSidebarActiveItem(index) {
            document.querySelectorAll('.search-result-item').forEach(item => {
                const itemIndex = parseInt(item.getAttribute('data-index'));
                if (itemIndex === index) {
                    item.classList.add('active');
                    item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                } else {
                    item.classList.remove('active');
                }
            });
        }
        
        // 高亮当前匹配项
        function highlightCurrent() {
            searchMatches.forEach((mark, index) => {
                if (index === currentSearchIndex) {
                    mark.classList.add('current');
                } else {
                    mark.classList.remove('current');
                }
            });
        }
        
        // 滚动到当前匹配项
        function scrollToCurrentMatch() {
            if (searchMatches[currentSearchIndex]) {
                const currentMark = searchMatches[currentSearchIndex];
                
                // 确保所在的collapsible内容是展开的
                const collapsibleContent = currentMark.closest('.collapsible-content');
                if (collapsibleContent) {
                    const collapsibleButton = collapsibleContent.previousElementSibling;
                    if (collapsibleButton && !collapsibleButton.classList.contains('active')) {
                        collapsibleButton.click();
                    }
                }
                
                // 滚动到视图中央
                currentMark.scrollIntoView({
                    behavior: 'smooth',
                    block: 'center'
                });
            }
        }
        
        // 更新搜索UI
        function updateSearchUI(total, current) {
            // 更新侧边栏的计数器和按钮状态（动态获取元素）
            const sidebarCounterEl = document.getElementById('sidebarCounter');
            const sidebarPrevBtnEl = document.getElementById('sidebarPrevBtn');
            const sidebarNextBtnEl = document.getElementById('sidebarNextBtn');
            const toggleBtn = document.getElementById('searchTogglePanel');
            
            if (sidebarCounterEl) {
                if (total === 0) {
                    sidebarCounterEl.textContent = '0/0';
                    if (sidebarPrevBtnEl) sidebarPrevBtnEl.classList.remove('active');
                    if (sidebarNextBtnEl) sidebarNextBtnEl.classList.remove('active');
                    // 隐藏切换按钮
                    if (toggleBtn) toggleBtn.classList.remove('visible');
                } else {
                    sidebarCounterEl.textContent = `${current}/${total}`;
                    if (sidebarPrevBtnEl) sidebarPrevBtnEl.classList.add('active');
                    if (sidebarNextBtnEl) sidebarNextBtnEl.classList.add('active');
                    // 显示切换按钮（桌面端可以手动关闭侧边栏后再打开）
                    if (toggleBtn) toggleBtn.classList.add('visible');
                }
            }
        }
        
        // 清除高亮
        function clearHighlights(element) {
            const marks = element.querySelectorAll('mark');
            marks.forEach(mark => {
                const parent = mark.parentNode;
                parent.replaceChild(document.createTextNode(mark.textContent), mark);
                parent.normalize();
            });
        }

        function highlightText(element, searchTerm) {
            const walker = document.createTreeWalker(
                element,
                NodeFilter.SHOW_TEXT,
                null,
                false
            );

            const nodesToReplace = [];
            let node;

            while (node = walker.nextNode()) {
                if (node.parentNode.nodeName !== 'SCRIPT' && 
                    node.parentNode.nodeName !== 'STYLE' &&
                    node.parentNode.nodeName !== 'MARK') {
                    const text = node.nodeValue;
                    const lowerText = text.toLowerCase();
                    
                    if (lowerText.includes(searchTerm)) {
                        nodesToReplace.push(node);
                    }
                }
            }

            nodesToReplace.forEach(node => {
                const text = node.nodeValue;
                const lowerText = text.toLowerCase();
                const fragment = document.createDocumentFragment();
                let lastIndex = 0;

                let index = lowerText.indexOf(searchTerm);
                while (index !== -1) {
                    if (index > lastIndex) {
                        fragment.appendChild(document.createTextNode(text.substring(lastIndex, index)));
                    }
                    
                    const mark = document.createElement('mark');
                    mark.textContent = text.substring(index, index + searchTerm.length);
                    fragment.appendChild(mark);
                    
                    lastIndex = index + searchTerm.length;
                    index = lowerText.indexOf(searchTerm, lastIndex);
                }

                if (lastIndex < text.length) {
                    fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
                }

                node.parentNode.replaceChild(fragment, node);
            });
        }
        
        // 提取搜索结果上下文
        function extractContext(markElement, maxLength = 80) {
            let text = markElement.textContent;
            let parent = markElement.parentNode;
            
            // 尝试获取父节点的文本内容
            while (parent && parent.textContent.length < maxLength) {
                parent = parent.parentNode;
                if (parent.classList && (
                    parent.classList.contains('collapsible-content') ||
                    parent.classList.contains('content')
                )) {
                    break;
                }
            }
            
            const fullText = parent ? parent.textContent : text;
            const markText = markElement.textContent;
            const markIndex = fullText.indexOf(markText);
            
            // 提取前后文本
            const before = fullText.substring(Math.max(0, markIndex - 40), markIndex);
            const after = fullText.substring(markIndex + markText.length, markIndex + markText.length + 40);
            
            // 清理空白字符
            const cleanBefore = before.replace(/\s+/g, ' ').trim();
            const cleanAfter = after.replace(/\s+/g, ' ').trim();
            
            return `${cleanBefore ? '...' + cleanBefore : ''}${'<mark>' + markText + '</mark>'}${cleanAfter ? cleanAfter + '...' : ''}`;
        }
        
        // 获取章节名称
        function getSectionName(element) {
            // 找到所属的tab
            let content = element.closest('.content');
            if (!content) return '未知章节';
            
            const contentId = content.id;
            const tabNames = ['⭐ AI技术详解', '💾 大数据技术详解', '☕ Java后端', '🎨 前端基础', '🎯 项目介绍话术', '💡 面试应对策略'];
            const tabIndex = parseInt(contentId.replace('content', '')) - 1;
            
            return tabNames[tabIndex] || '未知章节';
        }
        
        // 获取小节名称
        function getSubsectionName(element) {
            let heading = element.closest('.collapsible-content');
            if (heading) {
                heading = heading.previousElementSibling;
                if (heading && heading.classList.contains('collapsible-header')) {
                    return heading.textContent.trim();
                }
            }
            
            // 尝试找最近的h2/h3/h4
            let current = element;
            while (current && current !== document.body) {
                if (current.previousElementSibling) {
                    const prev = current.previousElementSibling;
                    if (prev.tagName && ['H2', 'H3', 'H4'].includes(prev.tagName)) {
                        return prev.textContent.trim();
                    }
                }
                current = current.parentElement;
            }
            
            return '';
        }
        
        // 渲染搜索结果列表到侧边栏
        function renderSearchResults() {
            // 等待DOM加载后再操作侧边栏元素
            setTimeout(function() {
                const sidebarList = document.getElementById('searchResultsList');
                const sidebarTitle = document.getElementById('searchResultsTitle');
                
                if (!sidebarList) return;
                
                if (searchResultsData.length === 0) {
                    sidebarList.innerHTML = `
                        <div class="search-results-empty">
                            <div class="search-results-empty-icon">🔍</div>
                            <p>输入关键词开始搜索</p>
                        </div>
                    `;
                    return;
                }
                
                // 按章节分组
                const grouped = {};
                searchResultsData.forEach((item, index) => {
                    const section = item.section || '其他';
                    if (!grouped[section]) {
                        grouped[section] = [];
                    }
                    grouped[section].push({ ...item, originalIndex: index });
                });
                
                // 生成HTML
                let html = '';
                Object.keys(grouped).forEach(section => {
                    html += `
                        <div class="search-results-group">
                            <div class="search-results-group-title">
                                📂 ${section} (${grouped[section].length})
                            </div>
                    `;
                    
                    grouped[section].forEach(item => {
                        const isActive = item.originalIndex === currentSearchIndex ? 'active' : '';
                        html += `
                            <div class="search-result-item ${isActive}" data-index="${item.originalIndex}">
                                <div class="search-result-section">
                                    ${item.subsection || ''}
                                </div>
                                <div class="search-result-context">
                                    ${item.context}
                                </div>
                            </div>
                        `;
                    });
                    
                    html += '</div>';
                });
                
                sidebarList.innerHTML = html;
                
                // 绑定点击事件
                document.querySelectorAll('.search-result-item').forEach(item => {
                    item.addEventListener('click', function() {
                        const index = parseInt(this.getAttribute('data-index'));
                        jumpToSearchResult(index);
                    });
                });
                
                // 更新标题
                if (sidebarTitle) {
                    sidebarTitle.textContent = `搜索结果 (${searchResultsData.length})`;
                }
            }, 0);
        }
        
        // 跳转到指定搜索结果
        function jumpToSearchResult(index) {
            if (index < 0 || index >= searchMatches.length) return;
            
            // 移除当前高亮
            if (searchMatches[currentSearchIndex]) {
                searchMatches[currentSearchIndex].classList.remove('current');
            }
            
            // 更新索引
            currentSearchIndex = index;
            
            // 高亮当前结果
            highlightCurrent();
            scrollToCurrentMatch();
            updateSearchUI(searchMatches.length, currentSearchIndex + 1);
            updateSidebarActiveItem(index);
        }
        
        // 暴露必要的变量和函数给其他script标签使用
        window.searchResultsData = searchResultsData;
        window.currentSearchIndex = currentSearchIndex;
        window.searchMatches = searchMatches;
        window.navigateSearch = navigateSearch;
        window.performSearch = performSearch;
        window.searchTogglePanel = searchTogglePanel;
        window.searchResultsPanel = searchResultsPanel;
        window.searchResultsList = searchResultsList;
        window.searchResultsTitle = searchResultsTitle;
        window.sidebarPrevBtn = sidebarPrevBtn;
        window.sidebarNextBtn = sidebarNextBtn;
        window.sidebarCounter = sidebarCounter;
        window.highlightCurrent = highlightCurrent;
        window.scrollToCurrentMatch = scrollToCurrentMatch;
        window.updateSearchUI = updateSearchUI;
        window.updateSidebarActiveItem = updateSidebarActiveItem;
        window.renderSearchResults = renderSearchResults;
        window.jumpToSearchResult = jumpToSearchResult;
        
        // 更新全局变量的方法
        window.updateGlobalSearchVars = function() {
            window.searchResultsData = searchResultsData;
            window.currentSearchIndex = currentSearchIndex;
            window.searchMatches = searchMatches;
        };
    </script>

    <!-- 移动端遮罩层 -->
    <div class="mobile-overlay" id="mobileOverlay"></div>

    <!-- 移动端底部抽屉卡片 -->
    <div class="mobile-tooltip-card" id="mobileTooltipCard">
        <button class="mobile-close-btn" onclick="closeMobileTooltip()">✕</button>
        <div class="mobile-tooltip-handle"></div>
        <div class="mobile-tooltip-content">
            <div class="mobile-term-title" id="mobileTerm">术语</div>
            <div class="mobile-pronunciation-section" id="mobilePronunciation" style="display:none;">
                <div class="mobile-pronunciation-label">🔊 读音</div>
                <div class="mobile-pronunciation-value" id="mobilePronunciationValue"></div>
            </div>
            <div class="mobile-explain-section" id="mobileExplain" style="display:none;">
                <div class="mobile-explain-label">❓ 知识点</div>
                <div class="mobile-explain-value" id="mobileExplainValue"></div>
            </div>
        </div>
    </div>

    <script>
        // 移动端抽屉卡片功能
        let touchStartY = 0;
        let touchEndY = 0;

        function showMobileTooltip(element) {
            const term = element.textContent.replace(/🔊|❓/g, '').trim();
            const pronunciation = element.getAttribute('data-pronunciation');
            const explain = element.getAttribute('data-explain');
            
            const card = document.getElementById('mobileTooltipCard');
            const overlay = document.getElementById('mobileOverlay');
            const termTitle = document.getElementById('mobileTerm');
            const pronunciationSection = document.getElementById('mobilePronunciation');
            const pronunciationValue = document.getElementById('mobilePronunciationValue');
            const explainSection = document.getElementById('mobileExplain');
            const explainValue = document.getElementById('mobileExplainValue');
            
            // 设置内容
            termTitle.textContent = term;
            
            if (pronunciation) {
                pronunciationSection.style.display = 'block';
                pronunciationValue.textContent = pronunciation;
            } else {
                pronunciationSection.style.display = 'none';
            }
            
            if (explain) {
                explainSection.style.display = 'block';
                explainValue.textContent = explain;
            } else {
                explainSection.style.display = 'none';
            }
            
            // 显示遮罩和卡片
            overlay.classList.add('show');
            card.classList.add('show');
            
            // 阻止body滚动
            document.body.style.overflow = 'hidden';
        }

        function closeMobileTooltip() {
            const card = document.getElementById('mobileTooltipCard');
            const overlay = document.getElementById('mobileOverlay');
            
            card.classList.remove('show');
            overlay.classList.remove('show');
            
            // 恢复body滚动
            document.body.style.overflow = '';
        }

        // 点击遮罩关闭
        document.getElementById('mobileOverlay').addEventListener('click', closeMobileTooltip);

        // 下滑关闭手势
        document.getElementById('mobileTooltipCard').addEventListener('touchstart', function(e) {
            touchStartY = e.touches[0].clientY;
        });

        document.getElementById('mobileTooltipCard').addEventListener('touchmove', function(e) {
            touchEndY = e.touches[0].clientY;
            const diff = touchEndY - touchStartY;
            
            // 如果向下滑动超过50px，开始移动卡片
            if (diff > 0) {
                this.style.transform = `translateY(${diff}px)`;
            }
        });

        document.getElementById('mobileTooltipCard').addEventListener('touchend', function(e) {
            const diff = touchEndY - touchStartY;
            
            // 如果向下滑动超过100px，关闭卡片
            if (diff > 100) {
                closeMobileTooltip();
            }
            
            // 重置位置
            this.style.transform = '';
            touchStartY = 0;
            touchEndY = 0;
        });

        // 创建波纹效果
        function createRipple(e, element) {
            const ripple = document.createElement('span');
            ripple.classList.add('ripple');
            
            const rect = element.getBoundingClientRect();
            const size = Math.max(rect.width, rect.height);
            const x = e.clientX - rect.left - size / 2;
            const y = e.clientY - rect.top - size / 2;
            
            ripple.style.width = ripple.style.height = size + 'px';
            ripple.style.left = x + 'px';
            ripple.style.top = y + 'px';
            
            element.appendChild(ripple);
            
            setTimeout(() => ripple.remove(), 600);
        }

        // 重写移动端点击逻辑，使用抽屉卡片
        document.addEventListener('DOMContentLoaded', function() {
            const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
            const isSmallScreen = window.innerWidth <= 768;
            
            if (isTouchDevice && isSmallScreen) {
                // 移除之前的点击事件监听器，添加新的
                document.addEventListener('click', function(e) {
                    const target = e.target.closest('.term, .explain');
                    
                    if (target) {
                        e.preventDefault();
                        e.stopPropagation();
                        
                        // 创建波纹效果
                        createRipple(e, target);
                        
                        // 显示移动端抽屉卡片
                        setTimeout(() => {
                            showMobileTooltip(target);
                        }, 200);
                    }
                }, true); // 使用捕获阶段，优先级更高
            }
        });
        
        // 电脑端：鼠标悬浮也显示卡片（带防抖延迟的智能关闭）
        let hoverTimeout;
        let closeTimeout;
        let currentHoverElement = null;
        
        // 鼠标进入关键字
        document.addEventListener('mouseenter', function(e) {
            const target = e.target.closest('.term, .explain');
            if (target && window.matchMedia('(hover: hover)').matches) {
                currentHoverElement = target;
                clearTimeout(hoverTimeout);
                clearTimeout(closeTimeout); // 取消关闭
                
                hoverTimeout = setTimeout(() => {
                    showMobileTooltip(target);
                }, 300); // 延迟300ms，避免快速划过时频繁弹出
            }
        }, true);
        
        // 全局鼠标移动监听 - 带防抖延迟
        document.addEventListener('mousemove', function(e) {
            if (!window.matchMedia('(hover: hover)').matches) return;
            
            const card = document.getElementById('mobileTooltipCard');
            if (!card.classList.contains('show')) return;
            
            // 检查鼠标是否在关键字或卡片上
            const onTerm = e.target.closest('.term, .explain');
            const onCard = e.target.closest('#mobileTooltipCard');
            
            // 清除之前的关闭计时器
            clearTimeout(closeTimeout);
            
            // 如果鼠标在有效区域内，不做任何操作
            if (onTerm || onCard) {
                return;
            }
            
            // 如果鼠标离开有效区域，延迟250ms后关闭
            closeTimeout = setTimeout(() => {
                // 再次检查，防止延迟期间鼠标又回到了有效区域
                const card = document.getElementById('mobileTooltipCard');
                const stillOnCard = card.matches(':hover');
                const stillOnTerm = currentHoverElement?.matches(':hover');
                
                if (!stillOnCard && !stillOnTerm) {
                    closeMobileTooltip();
                    currentHoverElement = null;
                }
            }, 250); // 250ms的容错时间
        });
    </script>

    <!-- 搜索结果面板切换按钮 -->
    <button class="search-toggle-panel" id="searchTogglePanel" title="显示搜索结果列表">📋</button>
    
    <!-- 搜索结果面板 -->
    <div class="search-results-panel" id="searchResultsPanel">
        <div class="search-results-header">
            <div class="search-results-header-top">
                <h3>
                    <span>📋</span>
                    <span id="searchResultsTitle">搜索结果</span>
                </h3>
                <button class="search-results-close" id="searchResultsClose">✕</button>
            </div>
            <!-- 侧边栏搜索输入框 -->
            <div class="sidebar-search-input-wrapper">
                <input type="text" 
                       id="sidebarSearchInput" 
                       class="sidebar-search-input" 
                       placeholder="🔍 搜索内容..."
                       autocomplete="off">
            </div>
            <div class="search-results-nav">
                <button class="search-results-nav-btn" id="sidebarPrevBtn" title="上一个（Shift+Enter）">↑</button>
                <div class="search-results-counter" id="sidebarCounter">0/0</div>
                <button class="search-results-nav-btn" id="sidebarNextBtn" title="下一个（Enter）">↓</button>
            </div>
        </div>
        <div class="search-results-list" id="searchResultsList">
            <div class="search-results-empty">
                <div class="search-results-empty-icon">🔍</div>
                <p>输入关键词开始搜索</p>
            </div>
        </div>
    </div>

    <script>
        // ==================== 搜索结果面板事件监听 ====================
        // 等待DOM完全加载后绑定侧边栏按钮事件
        document.addEventListener('DOMContentLoaded', function() {
            const searchTogglePanelBtn = document.getElementById('searchTogglePanel');
            const searchResultsPanelEl = document.getElementById('searchResultsPanel');
            const searchResultsCloseBtn = document.getElementById('searchResultsClose');
            const sidebarPrevButton = document.getElementById('sidebarPrevBtn');
            const sidebarNextButton = document.getElementById('sidebarNextBtn');
            
            // 双向同步顶部和侧边栏的搜索输入框
            const topSearchInput = document.getElementById('searchInput');
            const sidebarSearchInput = document.getElementById('sidebarSearchInput');
            
            // 防抖标志，避免无限循环
            let isSyncing = false;
            
            if (topSearchInput && sidebarSearchInput) {
                // 顶部输入框变化时同步到侧边栏
                topSearchInput.addEventListener('input', function() {
                    if (!isSyncing) {
                        isSyncing = true;
                        sidebarSearchInput.value = this.value;
                        isSyncing = false;
                    }
                });
                
                // 侧边栏输入框变化时同步到顶部
                sidebarSearchInput.addEventListener('input', function() {
                    if (!isSyncing) {
                        isSyncing = true;
                        topSearchInput.value = this.value;
                        // 触发顶部搜索框的input事件，执行搜索
                        const event = new Event('input', { bubbles: true });
                        topSearchInput.dispatchEvent(event);
                        isSyncing = false;
                    }
                });
                
                // 侧边栏输入框的键盘事件（Enter和Shift+Enter）
                sidebarSearchInput.addEventListener('keydown', function(e) {
                    if (e.key === 'Enter') {
                        e.preventDefault();
                        if (e.shiftKey) {
                            // Shift+Enter 上一个
                            if (sidebarPrevButton && !sidebarPrevButton.disabled) {
                                sidebarPrevButton.click();
                            }
                        } else {
                            // Enter 下一个
                            if (sidebarNextButton && !sidebarNextButton.disabled) {
                                sidebarNextButton.click();
                            }
                        }
                    }
                });
            }
            
            // 切换面板显示
            if (searchTogglePanelBtn) {
                searchTogglePanelBtn.addEventListener('click', function() {
                    if (searchResultsPanelEl) {
                        searchResultsPanelEl.classList.toggle('show');
                    }
                });
            }
            
            // 关闭面板
            if (searchResultsCloseBtn) {
                searchResultsCloseBtn.addEventListener('click', function() {
                    if (searchResultsPanelEl) {
                        searchResultsPanelEl.classList.remove('show');
                    }
                });
            }
            
            // 侧边栏导航按钮
            if (sidebarPrevButton) {
                sidebarPrevButton.addEventListener('click', function() {
                    if (window.navigateSearch) {
                        window.navigateSearch('prev');
                    }
                });
            }
            
            if (sidebarNextButton) {
                sidebarNextButton.addEventListener('click', function() {
                    if (window.navigateSearch) {
                        window.navigateSearch('next');
                    }
                });
            }
        });
        
        // ==================== 红黑树可视化演示 ====================
        function showRotateLeft() {
            const explanation = document.getElementById('treeExplanation');
            explanation.innerHTML = `
                <strong>🔄 左旋演示：</strong><br>
                节点15成为新的根，节点10变成15的左子节点。<br>
                <em>用途：</em>当右子树过高时，通过左旋降低右侧高度。<br>
                <span style="color: #e74c3c;">步骤：10 ← 15（逆时针旋转）</span>
            `;
            
            // 简单动画：高亮相关节点
            const svg = document.querySelector('#treeCanvas svg');
            svg.innerHTML = `
                <defs>
                    <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#e74c3c" />
                    </marker>
                </defs>
                
                <!-- 左旋前 -->
                <g opacity="0.3">
                    <text x="50" y="30" font-size="16" fill="#2c3e50" font-weight="bold">Before:</text>
                    <circle cx="120" cy="80" r="20" fill="#2c3e50"/>
                    <text x="120" y="86" text-anchor="middle" fill="white" font-size="16">10</text>
                    <line x1="140" y1="90" x2="180" y2="120" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="200" cy="140" r="20" fill="#e74c3c"/>
                    <text x="200" y="146" text-anchor="middle" fill="white" font-size="16">15</text>
                </g>
                
                <!-- 旋转箭头 -->
                <path d="M 250 110 Q 300 90 350 110" stroke="#e74c3c" stroke-width="3" fill="none" marker-end="url(#arrowhead)"/>
                <text x="300" y="85" text-anchor="middle" fill="#e74c3c" font-size="18" font-weight="bold">左旋 ↺</text>
                
                <!-- 左旋后 -->
                <g>
                    <text x="400" y="30" font-size="16" fill="#2c3e50" font-weight="bold">After:</text>
                    <circle cx="480" cy="80" r="20" fill="#e74c3c" class="node-animating"/>
                    <text x="480" y="86" text-anchor="middle" fill="white" font-size="16">15</text>
                    <line x1="460" y1="90" x2="420" y2="120" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="400" cy="140" r="20" fill="#2c3e50"/>
                    <text x="400" y="146" text-anchor="middle" fill="white" font-size="16">10</text>
                </g>
            `;
        }
        
        function showRotateRight() {
            const explanation = document.getElementById('treeExplanation');
            explanation.innerHTML = `
                <strong>🔄 右旋演示：</strong><br>
                节点5成为新的根，节点10变成5的右子节点。<br>
                <em>用途：</em>当左子树过高时，通过右旋降低左侧高度。<br>
                <span style="color: #e74c3c;">步骤：5 → 10（顺时针旋转）</span>
            `;
            
            const svg = document.querySelector('#treeCanvas svg');
            svg.innerHTML = `
                <defs>
                    <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#3498db" />
                    </marker>
                </defs>
                
                <!-- 右旋前 -->
                <g opacity="0.3">
                    <text x="50" y="30" font-size="16" fill="#2c3e50" font-weight="bold">Before:</text>
                    <circle cx="120" cy="80" r="20" fill="#2c3e50"/>
                    <text x="120" y="86" text-anchor="middle" fill="white" font-size="16">10</text>
                    <line x1="100" y1="90" x2="60" y2="120" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="40" cy="140" r="20" fill="#e74c3c"/>
                    <text x="40" y="146" text-anchor="middle" fill="white" font-size="16">5</text>
                </g>
                
                <!-- 旋转箭头 -->
                <path d="M 250 110 Q 300 90 350 110" stroke="#3498db" stroke-width="3" fill="none" marker-end="url(#arrowhead)"/>
                <text x="300" y="85" text-anchor="middle" fill="#3498db" font-size="18" font-weight="bold">右旋 ↻</text>
                
                <!-- 右旋后 -->
                <g>
                    <text x="400" y="30" font-size="16" fill="#2c3e50" font-weight="bold">After:</text>
                    <circle cx="480" cy="80" r="20" fill="#e74c3c" class="node-animating"/>
                    <text x="480" y="86" text-anchor="middle" fill="white" font-size="16">5</text>
                    <line x1="500" y1="90" x2="540" y2="120" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="560" cy="140" r="20" fill="#2c3e50"/>
                    <text x="560" y="146" text-anchor="middle" fill="white" font-size="16">10</text>
                </g>
            `;
        }
        
        function showRecolor() {
            const explanation = document.getElementById('treeExplanation');
            explanation.innerHTML = `
                <strong>🎨 变色演示：</strong><br>
                当插入节点导致连续红色节点时，需要变色维持红黑树性质。<br>
                <em>规则：</em>父节点和叔叔节点都是红色时，将父、叔变黑，祖父变红。<br>
                <span style="color: #f39c12;">步骤：红→黑，黑→红（交替变色）</span>
            `;
            
            const svg = document.querySelector('#treeCanvas svg');
            svg.innerHTML = `
                <!-- 变色前 -->
                <g opacity="0.3">
                    <text x="80" y="30" font-size="16" fill="#2c3e50" font-weight="bold">Before (违反规则):</text>
                    <circle cx="150" cy="70" r="22" fill="#2c3e50"/>
                    <text x="150" y="77" text-anchor="middle" fill="white" font-size="16">G</text>
                    
                    <line x1="150" y1="92" x2="100" y2="128" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="80" cy="150" r="20" fill="#e74c3c"/>
                    <text x="80" y="156" text-anchor="middle" fill="white" font-size="16">P</text>
                    
                    <line x1="150" y1="92" x2="200" y2="128" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="220" cy="150" r="20" fill="#e74c3c"/>
                    <text x="220" y="156" text-anchor="middle" fill="white" font-size="16">U</text>
                    
                    <line x1="68" y1="165" x2="40" y2="195" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="25" cy="210" r="18" fill="#e74c3c"/>
                    <text x="25" y="216" text-anchor="middle" fill="white" font-size="14">N</text>
                    <text x="25" y="235" text-anchor="middle" fill="#e74c3c" font-size="12">❌连续红色</text>
                </g>
                
                <!-- 箭头 -->
                <text x="300" y="140" text-anchor="middle" fill="#f39c12" font-size="28" font-weight="bold">🎨 变色</text>
                
                <!-- 变色后 -->
                <g>
                    <text x="400" y="30" font-size="16" fill="#2c3e50" font-weight="bold">After (平衡恢复):</text>
                    <circle cx="470" cy="70" r="22" fill="#e74c3c" class="node-color-change"/>
                    <text x="470" y="77" text-anchor="middle" fill="white" font-size="16">G</text>
                    <text x="470" y="50" text-anchor="middle" fill="#f39c12" font-size="11">变红↑</text>
                    
                    <line x1="470" y1="92" x2="420" y2="128" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="400" cy="150" r="20" fill="#2c3e50" class="node-color-change"/>
                    <text x="400" y="156" text-anchor="middle" fill="white" font-size="16">P</text>
                    <text x="400" y="175" text-anchor="middle" fill="#27ae60" font-size="11">变黑↓</text>
                    
                    <line x1="470" y1="92" x2="520" y2="128" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="540" cy="150" r="20" fill="#2c3e50" class="node-color-change"/>
                    <text x="540" y="156" text-anchor="middle" fill="white" font-size="16">U</text>
                    <text x="540" y="175" text-anchor="middle" fill="#27ae60" font-size="11">变黑↓</text>
                    
                    <line x1="388" y1="165" x2="360" y2="195" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="345" cy="210" r="18" fill="#e74c3c"/>
                    <text x="345" y="216" text-anchor="middle" fill="white" font-size="14">N</text>
                    <text x="345" y="235" text-anchor="middle" fill="#27ae60" font-size="12">✓规则满足</text>
                </g>
            `;
        }
        
        // ==================== 红黑树交互式动画系统 ====================
        let animationState = {
            type: null, // 'left', 'right', 'recolor'
            currentStep: 0,
            totalSteps: 5,
            isPlaying: false,
            speed: 1, // 0.5慢速, 1正常, 2快速
            timer: null,
            steps: []
        };
        
        // 左旋动画步骤定义
        const leftRotationSteps = [
            {
                name: '平衡的红黑树',
                description: '📍 初始状态：完整的平衡红黑树',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="450" y="25" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">步骤1：平衡的红黑树</text>
                        <!-- 完整的平衡树结构 -->
                        <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="450" y1="68" x2="620" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="450" cy="50" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="450" y="58" text-anchor="middle" fill="white" font-size="17" font-weight="bold">30</text>
                        
                        <line x1="280" y1="138" x2="180" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="280" y1="138" x2="360" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="280" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="280" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">15</text>
                        
                        <line x1="620" y1="138" x2="540" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="620" y1="138" x2="700" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="620" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="620" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">50</text>
                        
                        <circle cx="180" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="180" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">10</text>
                        
                        <circle cx="360" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="360" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">25</text>
                        
                        <circle cx="540" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="540" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">40</text>
                        
                        <circle cx="700" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="700" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">60</text>
                        
                        <text x="450" y="250" text-anchor="middle" font-size="13" fill="#666">平衡状态，树高4，满足红黑树所有规则</text>
                    `;
                }
            },
            {
                name: '插入节点45',
                description: '➕ 插入新节点45，作为40的右子节点（红色）',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="450" y="25" text-anchor="middle" font-size="16" fill="#e74c3c" font-weight="bold">步骤2：插入节点45（红色）</text>
                        <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="450" y1="68" x2="620" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="450" cy="50" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="450" y="58" text-anchor="middle" fill="white" font-size="17" font-weight="bold">30</text>
                        
                        <line x1="280" y1="138" x2="180" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="280" y1="138" x2="360" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="280" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="280" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">15</text>
                        
                        <line x1="620" y1="138" x2="540" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="620" y1="138" x2="700" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="620" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3" class="node-highlight"/>
                        <text x="620" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">50</text>
                        
                        <circle cx="180" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="180" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">10</text>
                        
                        <circle cx="360" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="360" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">25</text>
                        
                        <line x1="540" y1="218" x2="580" y2="262" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="540" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="540" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">40</text>
                        
                        <circle cx="700" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="700" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">60</text>
                        
                        <circle cx="600" cy="280" r="20" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="600" y="286" text-anchor="middle" fill="white" font-size="14" font-weight="bold">45</text>
                        <text x="600" y="305" fill="#f39c12" font-size="12" font-weight="bold">NEW!</text>
                        
                        <text x="450" y="340" text-anchor="middle" font-size="13" fill="#f44336">⚠️ 冲突！40(红) → 45(红)，违反"不能有连续红色"规则</text>
                    `;
                }
            },
            {
                name: '识别问题',
                description: '🔍 发现：父节点40(红)和叔节点60(红)都是红色',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="450" y="25" text-anchor="middle" font-size="16" fill="#f39c12" font-weight="bold">步骤3：分析问题 - 需要左旋</text>
                        <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="450" y1="68" x2="620" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="450" cy="50" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="450" y="58" text-anchor="middle" fill="white" font-size="17" font-weight="bold">30</text>
                        
                        <line x1="280" y1="138" x2="180" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="280" y1="138" x2="360" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="280" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="280" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">15</text>
                        
                        <line x1="620" y1="138" x2="540" y2="182" stroke="#95a5a6" stroke-width="2" class="link-highlight"/>
                        <line x1="620" y1="138" x2="700" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="620" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="620" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">50</text>
                        <text x="620" y="145" fill="#666" font-size="11">祖父(黑)</text>
                        
                        <circle cx="180" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="180" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">10</text>
                        
                        <circle cx="360" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="360" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">25</text>
                        
                        <line x1="540" y1="218" x2="600" y2="262" stroke="#95a5a6" stroke-width="2" class="link-highlight"/>
                        <circle cx="540" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="540" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">40</text>
                        <text x="540" y="225" fill="#e74c3c" font-size="11" font-weight="bold">父(红)</text>
                        
                        <circle cx="700" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="700" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">60</text>
                        <text x="700" y="225" fill="#e74c3c" font-size="11">叔(红)</text>
                        
                        <circle cx="600" cy="280" r="20" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="600" y="286" text-anchor="middle" fill="white" font-size="14" font-weight="bold">45</text>
                        <text x="600" y="305" fill="#e74c3c" font-size="11" font-weight="bold">新节点(红)</text>
                        
                        <text x="450" y="340" text-anchor="middle" font-size="13" fill="#f39c12">案例：RR型（右-右），需要对50做左旋 + 变色</text>
                    `;
                }
            },
            {
                name: '执行左旋',
                description: '🔄 对节点50执行左旋转，40上移为子树根',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="450" y="25" text-anchor="middle" font-size="16" fill="#3498db" font-weight="bold">步骤4：左旋 - 40上移</text>
                        <defs>
                            <marker id="rotateArrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                                <polygon points="0 0, 10 3, 0 6" fill="#3498db" />
                            </marker>
                        </defs>
                        <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="450" y1="68" x2="580" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="450" cy="50" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="450" y="58" text-anchor="middle" fill="white" font-size="17" font-weight="bold">30</text>
                        
                        <line x1="280" y1="138" x2="180" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="280" y1="138" x2="360" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="280" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="280" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">15</text>
                        
                        <line x1="580" y1="138" x2="650" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="580" y1="138" x2="720" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="580" cy="120" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-moving"/>
                        <text x="580" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">40</text>
                        <text x="580" y="145" fill="#3498db" font-size="11" font-weight="bold">↑上移</text>
                        
                        <circle cx="180" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="180" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">10</text>
                        
                        <circle cx="360" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="360" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">25</text>
                        
                        <line x1="650" y1="218" x2="600" y2="262" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="650" cy="200" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3" class="node-moving"/>
                        <text x="650" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">50</text>
                        <text x="650" y="225" fill="#3498db" font-size="11">↓下移</text>
                        
                        <line x1="720" y1="218" x2="760" y2="262" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="720" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="720" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">60</text>
                        
                        <circle cx="600" cy="280" r="20" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="600" y="286" text-anchor="middle" fill="white" font-size="14" font-weight="bold">45</text>
                        
                        <circle cx="760" cy="280" r="20" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="760" y="286" text-anchor="middle" fill="white" font-size="14" font-weight="bold">70</text>
                        
                        <path d="M 540 200 Q 560 160 580 140" stroke="#3498db" stroke-width="3" fill="none" marker-end="url(#rotateArrow)"/>
                        
                        <text x="450" y="340" text-anchor="middle" font-size="13" fill="#3498db">40成为子树根，50成为40的右子节点</text>
                    `;
                }
            },
            {
                name: '完成 + 变色',
                description: '✅ 左旋完成！对40和50变色以满足规则',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="450" y="25" text-anchor="middle" font-size="16" fill="#27ae60" font-weight="bold">步骤5：旋转完成 + 变色修复</text>
                        <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="450" y1="68" x2="580" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="450" cy="50" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="450" y="58" text-anchor="middle" fill="white" font-size="17" font-weight="bold">30</text>
                        
                        <line x1="280" y1="138" x2="180" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="280" y1="138" x2="360" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="280" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="280" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">15</text>
                        
                        <line x1="580" y1="138" x2="650" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="580" y1="138" x2="720" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="580" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="580" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">40</text>
                        <text x="580" y="100" fill="#27ae60" font-size="11" font-weight="bold">变黑✓</text>
                        
                        <circle cx="180" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="180" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">10</text>
                        
                        <circle cx="360" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="360" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">25</text>
                        
                        <line x1="650" y1="218" x2="600" y2="262" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="650" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="650" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">50</text>
                        <text x="650" y="180" fill="#27ae60" font-size="11" font-weight="bold">变红✓</text>
                        
                        <line x1="720" y1="218" x2="760" y2="262" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="720" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="720" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">60</text>
                        
                        <circle cx="600" cy="280" r="20" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="600" y="286" text-anchor="middle" fill="white" font-size="14" font-weight="bold">45</text>
                        
                        <circle cx="760" cy="280" r="20" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="760" y="286" text-anchor="middle" fill="white" font-size="14" font-weight="bold">70</text>
                        
                        <text x="100" y="150" fill="#27ae60" font-size="12">树高: 4</text>
                        <text x="100" y="170" fill="#27ae60" font-size="12">黑高: 3</text>
                        
                        <text x="450" y="340" text-anchor="middle" font-size="13" fill="#27ae60" font-weight="bold">✓ 平衡恢复！左旋+变色解决了连续红色节点问题</text>
                    `;
                }
            }
        ];
        
        // 右旋动画步骤定义
        const rightRotationSteps = [
            {
                name: '平衡的红黑树',
                description: '📍 初始状态：完整的平衡红黑树',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="450" y="25" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">步骤1：平衡的红黑树</text>
                        <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="450" y1="68" x2="620" y2="110" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="450" cy="50" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="450" y="58" text-anchor="middle" fill="white" font-size="17" font-weight="bold">30</text>
                        
                        <line x1="280" y1="138" x2="180" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="280" y1="138" x2="380" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="280" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3" class="node-highlight"/>
                        <text x="280" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">15</text>
                        
                        <line x1="620" y1="138" x2="520" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="620" y1="138" x2="720" y2="182" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="620" cy="120" r="24" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="620" y="127" text-anchor="middle" fill="white" font-size="16" font-weight="bold">50</text>
                        
                        <circle cx="180" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="180" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">10</text>
                        
                        <circle cx="380" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="380" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">25</text>
                        
                        <circle cx="520" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="520" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">40</text>
                        
                        <circle cx="720" cy="200" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="720" y="206" text-anchor="middle" fill="white" font-size="15" font-weight="bold">60</text>
                        
                        <text x="450" y="250" text-anchor="middle" font-size="13" fill="#666">平衡状态，树高4，满足红黑树所有规则</text>
                    `;
                }
            },
            {
                name: '断开连接',
                description: '✂️ 断开10→5的连接，准备重新组织',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#3498db" font-weight="bold">步骤2：断开连接</text>
                        <line x1="300" y1="75" x2="200" y2="125" stroke="#95a5a6" stroke-width="2" opacity="0.3" stroke-dasharray="5,5"/>
                        <circle cx="300" cy="50" r="25" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="300" y="58" text-anchor="middle" fill="white" font-size="20" font-weight="bold">10</text>
                        <circle cx="200" cy="150" r="25" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="200" y="158" text-anchor="middle" fill="white" font-size="20" font-weight="bold">5</text>
                        <text x="250" y="95" text-anchor="middle" font-size="18" fill="#3498db">✂️</text>
                        <text x="300" y="260" text-anchor="middle" font-size="13" fill="#555">断开父子关系，5准备上移</text>
                    `;
                }
            },
            {
                name: '节点旋转',
                description: '🔄 5上移成为新根，10下降为右子节点',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#3498db" font-weight="bold">步骤3：节点旋转中...</text>
                        <defs>
                            <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                                <polygon points="0 0, 10 3, 0 6" fill="#3498db" />
                            </marker>
                        </defs>
                        <path d="M 200 150 Q 200 80 250 50" stroke="#3498db" stroke-width="3" fill="none" marker-end="url(#arrowhead)" stroke-dasharray="5,5"/>
                        <text x="180" y="100" font-size="16" fill="#3498db" font-weight="bold">↑</text>
                        <circle cx="300" cy="50" r="25" fill="#2c3e50" stroke="#34495e" stroke-width="3" opacity="0.5"/>
                        <text x="300" y="58" text-anchor="middle" fill="white" font-size="20" font-weight="bold">10</text>
                        <circle cx="250" cy="50" r="25" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-moving"/>
                        <text x="250" y="58" text-anchor="middle" fill="white" font-size="20" font-weight="bold">5</text>
                        <text x="300" y="260" text-anchor="middle" font-size="13" fill="#555">5顺时针旋转上移到根位置</text>
                    `;
                }
            },
            {
                name: '重建连接',
                description: '🔗 建立新的父子关系：5→10',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#3498db" font-weight="bold">步骤4：重建连接</text>
                        <line x1="250" y1="75" x2="350" y2="125" stroke="#27ae60" stroke-width="3" class="link-highlight"/>
                        <circle cx="250" cy="50" r="25" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="250" y="58" text-anchor="middle" fill="white" font-size="20" font-weight="bold">5</text>
                        <circle cx="350" cy="150" r="25" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="350" y="158" text-anchor="middle" fill="white" font-size="20" font-weight="bold">10</text>
                        <text x="300" y="100" text-anchor="middle" font-size="18" fill="#27ae60">✓</text>
                        <text x="300" y="260" text-anchor="middle" font-size="13" fill="#555">连接5→10，新关系建立</text>
                    `;
                }
            },
            {
                name: '完成',
                description: '✅ 右旋完成！5成为新根，10是其右子节点',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#27ae60" font-weight="bold">步骤5：右旋完成！</text>
                        <line x1="250" y1="75" x2="350" y2="125" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="250" cy="50" r="25" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="250" y="58" text-anchor="middle" fill="white" font-size="20" font-weight="bold">5</text>
                        <text x="250" y="95" text-anchor="middle" font-size="12" fill="#27ae60" font-weight="bold">新根</text>
                        <circle cx="350" cy="150" r="25" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="350" y="158" text-anchor="middle" fill="white" font-size="20" font-weight="bold">10</text>
                        <text x="350" y="195" text-anchor="middle" font-size="12" fill="#555">右子节点</text>
                        <text x="300" y="260" text-anchor="middle" font-size="13" fill="#27ae60" font-weight="bold">✓ 旋转完成，树平衡性改善</text>
                    `;
                }
            }
        ];
        
        // 变色动画步骤定义
        const recolorSteps = [
            {
                name: '初始状态',
                description: '📍 违反规则：连续的红色节点',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#9b59b6" font-weight="bold">步骤1：发现问题</text>
                        <circle cx="300" cy="80" r="25" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="300" y="88" text-anchor="middle" fill="white" font-size="20" font-weight="bold">G</text>
                        <line x1="300" y1="105" x2="230" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="300" y1="105" x2="370" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="230" cy="170" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="230" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">P</text>
                        <circle cx="370" cy="170" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="370" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">U</text>
                        <line x1="230" y1="192" x2="200" y2="225" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="200" cy="240" r="18" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="200" y="246" text-anchor="middle" fill="white" font-size="14" font-weight="bold">N</text>
                        <text x="300" y="280" text-anchor="middle" font-size="13" fill="#e74c3c">⚠️ P和N都是红色，违反规则！</text>
                    `;
                }
            },
            {
                name: '识别叔叔',
                description: '🔍 检查叔叔节点U的颜色',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#9b59b6" font-weight="bold">步骤2：检查叔叔节点</text>
                        <circle cx="300" cy="80" r="25" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="300" y="88" text-anchor="middle" fill="white" font-size="20" font-weight="bold">G</text>
                        <line x1="300" y1="105" x2="230" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="300" y1="105" x2="370" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="230" cy="170" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="230" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">P</text>
                        <circle cx="370" cy="170" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="370" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">U</text>
                        <text x="370" y="205" text-anchor="middle" font-size="12" fill="#e74c3c" font-weight="bold">叔叔也是红色</text>
                        <line x1="230" y1="192" x2="200" y2="225" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="200" cy="240" r="18" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="200" y="246" text-anchor="middle" fill="white" font-size="14" font-weight="bold">N</text>
                        <text x="300" y="280" text-anchor="middle" font-size="13" fill="#555">父P和叔U都是红色→可以变色解决</text>
                    `;
                }
            },
            {
                name: '父叔变黑',
                description: '🎨 将父节点P和叔叔节点U变为黑色',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#9b59b6" font-weight="bold">步骤3：父叔变黑</text>
                        <circle cx="300" cy="80" r="25" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="300" y="88" text-anchor="middle" fill="white" font-size="20" font-weight="bold">G</text>
                        <line x1="300" y1="105" x2="230" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="300" y1="105" x2="370" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="230" cy="170" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3" class="node-highlight"/>
                        <text x="230" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">P</text>
                        <text x="230" y="205" text-anchor="middle" font-size="11" fill="#27ae60" font-weight="bold">变黑↓</text>
                        <circle cx="370" cy="170" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3" class="node-highlight"/>
                        <text x="370" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">U</text>
                        <text x="370" y="205" text-anchor="middle" font-size="11" fill="#27ae60" font-weight="bold">变黑↓</text>
                        <line x1="230" y1="192" x2="200" y2="225" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="200" cy="240" r="18" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="200" y="246" text-anchor="middle" fill="white" font-size="14" font-weight="bold">N</text>
                        <text x="300" y="280" text-anchor="middle" font-size="13" fill="#27ae60">✓ P和U变黑，减少红色节点</text>
                    `;
                }
            },
            {
                name: '祖父变红',
                description: '🎨 将祖父节点G变为红色（如果不是根）',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#9b59b6" font-weight="bold">步骤4：祖父变红</text>
                        <circle cx="300" cy="80" r="25" fill="#e74c3c" stroke="#c0392b" stroke-width="3" class="node-highlight"/>
                        <text x="300" y="88" text-anchor="middle" fill="white" font-size="20" font-weight="bold">G</text>
                        <text x="300" y="115" text-anchor="middle" font-size="11" fill="#e74c3c" font-weight="bold">变红↑</text>
                        <line x1="300" y1="105" x2="230" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="300" y1="105" x2="370" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="230" cy="170" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="230" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">P</text>
                        <circle cx="370" cy="170" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="370" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">U</text>
                        <line x1="230" y1="192" x2="200" y2="225" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="200" cy="240" r="18" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="200" y="246" text-anchor="middle" fill="white" font-size="14" font-weight="bold">N</text>
                        <text x="300" y="280" text-anchor="middle" font-size="13" fill="#555">G变红，保持黑色节点数量</text>
                    `;
                }
            },
            {
                name: '完成',
                description: '✅ 变色完成，红黑树性质恢复',
                action: () => {
                    const svg = document.querySelector('#treeCanvas svg');
                    svg.innerHTML = `
                        <text x="300" y="30" text-anchor="middle" font-size="14" fill="#27ae60" font-weight="bold">步骤5：变色完成！</text>
                        <circle cx="300" cy="80" r="25" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="300" y="88" text-anchor="middle" fill="white" font-size="20" font-weight="bold">G</text>
                        <line x1="300" y1="105" x2="230" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <line x1="300" y1="105" x2="370" y2="145" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="230" cy="170" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="230" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">P</text>
                        <text x="230" y="205" text-anchor="middle" font-size="11" fill="#27ae60">⚫黑</text>
                        <circle cx="370" cy="170" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                        <text x="370" y="178" text-anchor="middle" fill="white" font-size="18" font-weight="bold">U</text>
                        <text x="370" y="205" text-anchor="middle" font-size="11" fill="#27ae60">⚫黑</text>
                        <line x1="230" y1="192" x2="200" y2="225" stroke="#95a5a6" stroke-width="2"/>
                        <circle cx="200" cy="240" r="18" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                        <text x="200" y="246" text-anchor="middle" fill="white" font-size="14" font-weight="bold">N</text>
                        <text x="200" y="270" text-anchor="middle" font-size="11" fill="#27ae60">🔴红</text>
                        <text x="300" y="280" text-anchor="middle" font-size="13" fill="#27ae60" font-weight="bold">✓ 没有连续红色节点，规则满足！</text>
                    `;
                }
            }
        ];
        
        // 启动动画
        function startRotateLeft() {
            initAnimation('left', leftRotationSteps, '左旋动画：15成为新根，10下降为左子节点');
        }
        
        function startRotateRight() {
            initAnimation('right', rightRotationSteps, '右旋动画：5成为新根，10下降为右子节点');
        }
        
        function startRecolor() {
            initAnimation('recolor', recolorSteps, '变色动画：通过改变节点颜色维持红黑树性质');
        }
        
        function initAnimation(type, steps, description) {
            animationState.type = type;
            animationState.steps = steps;
            animationState.currentStep = 0;
            animationState.totalSteps = steps.length;
            animationState.isPlaying = false;
            
            // 显示控制面板
            document.getElementById('animationControls').style.display = 'flex';
            
            // 更新说明
            const explanation = document.getElementById('treeExplanation');
            explanation.innerHTML = `<strong>${description}</strong><br><em>使用下方按钮控制动画播放</em>`;
            
            // 执行第一步
            executeStep(0);
            updateControls();
        }
        
        function executeStep(stepIndex) {
            if (stepIndex < 0 || stepIndex >= animationState.totalSteps) return;
            
            animationState.currentStep = stepIndex;
            const step = animationState.steps[stepIndex];
            
            // 执行动画
            step.action();
            
            // 更新步骤指示器
            document.getElementById('stepIndicator').textContent = 
                `步骤 ${stepIndex + 1}/${animationState.totalSteps}: ${step.name}`;
            
            // 更新说明
            const explanation = document.getElementById('treeExplanation');
            const currentDesc = explanation.querySelector('strong').textContent;
            explanation.innerHTML = `
                <strong>${currentDesc}</strong><br>
                <span style="color: #667eea; font-weight: 600;">${step.description}</span>
            `;
            
            updateControls();
        }
        
        function updateControls() {
            const prevBtn = document.getElementById('prevStepBtn');
            const nextBtn = document.getElementById('nextStepBtn');
            const playBtn = document.getElementById('playPauseBtn');
            
            // 更新按钮状态
            prevBtn.disabled = animationState.currentStep === 0;
            nextBtn.disabled = animationState.currentStep === animationState.totalSteps - 1;
            
            // 更新播放按钮文字
            playBtn.textContent = animationState.isPlaying ? '⏸ 暂停' : '▶ 播放';
        }
        
        function prevStep() {
            if (animationState.currentStep > 0) {
                stopAutoPlay();
                executeStep(animationState.currentStep - 1);
            }
        }
        
        function nextStep() {
            if (animationState.currentStep < animationState.totalSteps - 1) {
                executeStep(animationState.currentStep + 1);
            } else {
                stopAutoPlay();
            }
        }
        
        function togglePlayPause() {
            if (animationState.isPlaying) {
                stopAutoPlay();
            } else {
                startAutoPlay();
            }
        }
        
        function startAutoPlay() {
            animationState.isPlaying = true;
            updateControls();
            
            const baseDelay = 2000; // 基础延迟2秒
            const delay = baseDelay / animationState.speed;
            
            animationState.timer = setInterval(() => {
                if (animationState.currentStep < animationState.totalSteps - 1) {
                    nextStep();
                } else {
                    stopAutoPlay();
                }
            }, delay);
        }
        
        function stopAutoPlay() {
            animationState.isPlaying = false;
            if (animationState.timer) {
                clearInterval(animationState.timer);
                animationState.timer = null;
            }
            updateControls();
        }
        
        function setSpeed(speed) {
            animationState.speed = speed;
            
            // 更新速度按钮样式
            document.querySelectorAll('.speed-btn').forEach(btn => {
                btn.classList.remove('active');
            });
            event.target.classList.add('active');
            
            // 如果正在播放，重启定时器
            if (animationState.isPlaying) {
                stopAutoPlay();
                startAutoPlay();
            }
        }
        
        function resetTree() {
            // 停止动画
            stopAutoPlay();
            
            // 隐藏控制面板
            document.getElementById('animationControls').style.display = 'none';
            
            // 重置状态
            animationState = {
                type: null,
                currentStep: 0,
                totalSteps: 5,
                isPlaying: false,
                speed: 1,
                timer: null,
                steps: []
            };
            
            // 恢复初始树（11个节点的完整平衡红黑树）
            const svg = document.querySelector('#treeCanvas svg');
            svg.innerHTML = `
                <g id="tree-initial">
                    <!-- 第1层：根节点 (黑) -->
                    <circle cx="450" cy="40" r="28" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                    <text x="450" y="48" text-anchor="middle" fill="white" font-size="18" font-weight="bold">30</text>
                    
                    <!-- 第2层 -->
                    <line x1="450" y1="68" x2="280" y2="110" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="280" cy="120" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                    <text x="280" y="128" text-anchor="middle" fill="white" font-size="17" font-weight="bold">15</text>
                    
                    <line x1="450" y1="68" x2="620" y2="110" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="620" cy="120" r="26" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                    <text x="620" y="128" text-anchor="middle" fill="white" font-size="17" font-weight="bold">50</text>
                    
                    <!-- 第3层 -->
                    <line x1="280" y1="146" x2="180" y2="190" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="180" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                    <text x="180" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">10</text>
                    
                    <line x1="280" y1="146" x2="380" y2="190" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="380" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                    <text x="380" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">25</text>
                    
                    <line x1="620" y1="146" x2="520" y2="190" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="520" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                    <text x="520" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">40</text>
                    
                    <line x1="620" y1="146" x2="720" y2="190" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="720" cy="200" r="24" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                    <text x="720" y="207" text-anchor="middle" fill="white" font-size="16" font-weight="bold">60</text>
                    
                    <!-- 第4层 -->
                    <line x1="180" y1="224" x2="120" y2="270" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="120" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                    <text x="120" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">5</text>
                    
                    <line x1="180" y1="224" x2="240" y2="270" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="240" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                    <text x="240" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">12</text>
                    
                    <line x1="720" y1="224" x2="680" y2="270" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="680" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                    <text x="680" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">55</text>
                    
                    <line x1="720" y1="224" x2="760" y2="270" stroke="#95a5a6" stroke-width="2"/>
                    <circle cx="760" cy="280" r="22" fill="#2c3e50" stroke="#34495e" stroke-width="3"/>
                    <text x="760" y="286" text-anchor="middle" fill="white" font-size="15" font-weight="bold">70</text>
                    
                    <!-- 树高度标注 -->
                    <text x="50" y="150" fill="#666" font-size="13">树高: 4</text>
                    <text x="50" y="170" fill="#666" font-size="13">黑高: 3</text>
                </g>
            `;
            
            // 恢复说明
            document.getElementById('treeExplanation').innerHTML = `
                <p><span style="color: #2c3e50;">⚫</span> 黑色节点 &nbsp;&nbsp; <span style="color: #e74c3c;">🔴</span> 红色节点</p>
                <p>当前是平衡的红黑树，点击上方按钮查看插入新节点后的修复过程</p>
            `;
        }
        
        // ==================== JVM内存可视化 ====================
        function showJVMMemory() {
            const explanation = document.getElementById('jvmExplanation');
            explanation.innerHTML = `
                <strong>📊 JVM内存布局</strong><br>
                <span style="color: #4caf50;">●绿色：堆（Heap）</span> - 对象实例存储，GC主要工作区<br>
                <span style="color: #03a9f4;">●蓝色：方法区</span> - 类元数据、常量池<br>
                <span style="color: #e91e63;">●粉色：栈</span> - 局部变量、方法调用栈帧<br>
                <em>提示：堆和方法区是线程共享，栈和程序计数器是线程私有</em>
            `;
        }
        
        function showObjectCreation() {
            const svg = document.querySelector('#jvmCanvas svg');
            svg.innerHTML = `
                <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">对象创建过程演示</text>
                
                <!-- 代码区 -->
                <rect x="50" y="60" width="200" height="80" fill="#f5f5f5" stroke="#666" stroke-width="2" rx="5"/>
                <text x="150" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">代码</text>
                <text x="60" y="110" font-size="11" fill="#e91e63" font-family="monospace">User user = </text>
                <text x="60" y="125" font-size="11" fill="#03a9f4" font-family="monospace">new User();</text>
                
                <!-- 箭头1：栈 -->
                <path d="M 250 90 L 310 90" stroke="#e91e63" stroke-width="3" fill="none" marker-end="url(#arrow1)"/>
                <text x="280" y="85" font-size="11" fill="#e91e63" font-weight="bold">1️⃣</text>
                <defs>
                    <marker id="arrow1" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#e91e63" />
                    </marker>
                </defs>
                
                <!-- 栈帧 -->
                <rect x="310" y="60" width="130" height="80" fill="#fce4ec" stroke="#e91e63" stroke-width="3" rx="5"/>
                <text x="375" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">栈</text>
                <text x="320" y="110" font-size="11" fill="#2c3e50">变量: user</text>
                <text x="320" y="125" font-size="11" fill="#e91e63" font-family="monospace">引用→0x1234</text>
                
                <!-- 箭头2：堆 -->
                <path d="M 375 140 Q 375 170 190 200" stroke="#4caf50" stroke-width="3" fill="none" marker-end="url(#arrow2)" stroke-dasharray="5,5"/>
                <text x="300" y="160" font-size="11" fill="#4caf50" font-weight="bold">2️⃣ 指向</text>
                <defs>
                    <marker id="arrow2" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#4caf50" />
                    </marker>
                </defs>
                
                <!-- 堆对象 -->
                <rect x="50" y="200" width="280" height="120" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="5"/>
                <text x="190" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">堆（Heap）</text>
                <rect x="70" y="240" width="240" height="70" fill="#c8e6c9" stroke="#66bb6a" stroke-width="2" rx="3"/>
                <text x="190" y="260" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">User对象实例 (0x1234)</text>
                <text x="80" y="280" font-size="10" fill="#2c3e50">name: "张三"</text>
                <text x="80" y="295" font-size="10" fill="#2c3e50">age: 25</text>
                
                <!-- 方法区 -->
                <rect x="450" y="200" width="200" height="120" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="5"/>
                <text x="550" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">方法区</text>
                <rect x="470" y="240" width="160" height="70" fill="#b3e5fc" stroke="#0288d1" stroke-width="1"/>
                <text x="550" y="260" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">User类元数据</text>
                <text x="480" y="280" font-size="10" fill="#2c3e50">类结构信息</text>
                <text x="480" y="295" font-size="10" fill="#2c3e50">方法信息</text>
                
                <!-- 箭头3：类信息 -->
                <path d="M 330 270 L 450 270" stroke="#03a9f4" stroke-width="2" fill="none" marker-end="url(#arrow3)" stroke-dasharray="3,3"/>
                <text x="380" y="265" font-size="10" fill="#03a9f4">3️⃣ 类型信息</text>
                <defs>
                    <marker id="arrow3" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#03a9f4" />
                    </marker>
                </defs>
                
                <!-- 说明 -->
                <text x="350" y="360" text-anchor="middle" font-size="12" fill="#666">1️⃣ 栈中存储引用 → 2️⃣ 引用指向堆中对象 → 3️⃣ 对象关联方法区中的类信息</text>
            `;
            
            const explanation = document.getElementById('jvmExplanation');
            explanation.innerHTML = `
                <strong>🎨 对象创建三步骤</strong><br>
                1️⃣ <strong>栈</strong>：创建局部变量user，存储对象引用（0x1234）<br>
                2️⃣ <strong>堆</strong>：分配内存空间，创建User对象实例，存储字段值<br>
                3️⃣ <strong>方法区</strong>：对象关联User类的元数据信息<br>
                <em style="color: #e91e63;">面试要点：栈存引用，堆存对象，方法区存类</em>
            `;
        }
        
        function resetJVMView() {
            const svg = document.querySelector('#jvmCanvas svg');
            svg.innerHTML = `
                <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">JVM运行时数据区</text>
                <rect x="50" y="60" width="280" height="280" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="190" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">堆（Heap）</text>
                <text x="190" y="105" text-anchor="middle" font-size="12" fill="#666">线程共享，GC主战场</text>
                <rect x="70" y="120" width="240" height="100" fill="#c8e6c9" stroke="#66bb6a" stroke-width="2" rx="5"/>
                <text x="190" y="140" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">新生代（Young）</text>
                <rect x="80" y="150" width="70" height="60" fill="#81c784" stroke="#388e3c" stroke-width="1"/>
                <text x="115" y="170" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">Eden</text>
                <text x="115" y="185" text-anchor="middle" font-size="10" fill="#fff">80%</text>
                <rect x="160" y="150" width="65" height="60" fill="#a5d6a7" stroke="#388e3c" stroke-width="1"/>
                <text x="192" y="170" text-anchor="middle" font-size="11" fill="#2c3e50">S0</text>
                <text x="192" y="185" text-anchor="middle" font-size="10" fill="#2c3e50">10%</text>
                <rect x="235" y="150" width="65" height="60" fill="#a5d6a7" stroke="#388e3c" stroke-width="1"/>
                <text x="267" y="170" text-anchor="middle" font-size="11" fill="#2c3e50">S1</text>
                <text x="267" y="185" text-anchor="middle" font-size="10" fill="#2c3e50">10%</text>
                <rect x="70" y="235" width="240" height="90" fill="#fff9c4" stroke="#fbc02d" stroke-width="2" rx="5"/>
                <text x="190" y="260" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">老年代（Old）</text>
                <text x="190" y="280" text-anchor="middle" font-size="11" fill="#666">长期存活对象</text>
                <text x="190" y="295" text-anchor="middle" font-size="11" fill="#666">经历15次GC</text>
                <rect x="360" y="60" width="280" height="120" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="8"/>
                <text x="500" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">方法区（MetaSpace）</text>
                <text x="500" y="105" text-anchor="middle" font-size="12" fill="#666">类信息、常量、静态变量</text>
                <rect x="380" y="115" width="230" height="50" fill="#b3e5fc" stroke="#0288d1" stroke-width="1"/>
                <text x="495" y="135" text-anchor="middle" font-size="11" fill="#2c3e50">类元数据</text>
                <text x="495" y="150" text-anchor="middle" font-size="10" fill="#666">Class信息、字段、方法</text>
                <rect x="360" y="200" width="130" height="140" fill="#fce4ec" stroke="#e91e63" stroke-width="3" rx="8"/>
                <text x="425" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">栈（Stack）</text>
                <text x="425" y="240" text-anchor="middle" font-size="11" fill="#666">线程私有</text>
                <rect x="375" y="250" width="100" height="25" fill="#f8bbd0" stroke="#c2185b" stroke-width="1"/>
                <text x="425" y="267" text-anchor="middle" font-size="10" fill="#2c3e50">栈帧3</text>
                <rect x="375" y="280" width="100" height="25" fill="#f8bbd0" stroke="#c2185b" stroke-width="1"/>
                <text x="425" y="297" text-anchor="middle" font-size="10" fill="#2c3e50">栈帧2</text>
                <rect x="375" y="310" width="100" height="25" fill="#f48fb1" stroke="#c2185b" stroke-width="1"/>
                <text x="425" y="327" text-anchor="middle" font-size="10" fill="#fff" font-weight="bold">栈帧1</text>
                <rect x="510" y="200" width="130" height="65" fill="#f3e5f5" stroke="#9c27b0" stroke-width="3" rx="8"/>
                <text x="575" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">程序计数器</text>
                <text x="575" y="245" text-anchor="middle" font-size="11" fill="#666">当前指令地址</text>
                <rect x="510" y="275" width="130" height="65" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="575" y="300" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">本地方法栈</text>
                <text x="575" y="320" text-anchor="middle" font-size="11" fill="#666">Native方法</text>
                <text x="350" y="370" text-anchor="middle" font-size="12" fill="#666">线程共享：堆、方法区 | 线程私有：栈、程序计数器、本地方法栈</text>
            `;
            
            document.getElementById('jvmExplanation').innerHTML = '点击按钮查看内存布局或对象创建过程';
        }
        
        // ==================== GC算法可视化 ====================
        function showMarkSweep() {
            const svg = document.querySelector('#gcCanvas svg');
            svg.innerHTML = `
                <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">标记-清除算法（Mark-Sweep）</text>
                
                <!-- GC前 -->
                <text x="100" y="70" font-size="14" fill="#2c3e50" font-weight="bold">GC前：</text>
                <rect x="50" y="80" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="75" y="110" text-anchor="middle" fill="#fff" font-weight="bold">A</text>
                <text x="75" y="125" text-anchor="middle" fill="#fff" font-size="10">存活</text>
                
                <rect x="110" y="80" width="50" height="50" fill="#e0e0e0" stroke="#9e9e9e" stroke-width="2"/>
                <text x="135" y="110" text-anchor="middle" fill="#666" font-weight="bold">B</text>
                <text x="135" y="125" text-anchor="middle" fill="#666" font-size="10">垃圾</text>
                
                <rect x="170" y="80" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="195" y="110" text-anchor="middle" fill="#fff" font-weight="bold">C</text>
                <text x="195" y="125" text-anchor="middle" fill="#fff" font-size="10">存活</text>
                
                <rect x="230" y="80" width="50" height="50" fill="#e0e0e0" stroke="#9e9e9e" stroke-width="2"/>
                <text x="255" y="110" text-anchor="middle" fill="#666" font-weight="bold">D</text>
                <text x="255" y="125" text-anchor="middle" fill="#666" font-size="10">垃圾</text>
                
                <rect x="290" y="80" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="315" y="110" text-anchor="middle" fill="#fff" font-weight="bold">E</text>
                <text x="315" y="125" text-anchor="middle" fill="#fff" font-size="10">存活</text>
                
                <!-- 箭头 -->
                <text x="175" y="165" text-anchor="middle" font-size="16" fill="#e91e63" font-weight="bold">↓</text>
                <text x="175" y="185" text-anchor="middle" font-size="12" fill="#e91e63">清除垃圾对象</text>
                
                <!-- GC后 -->
                <text x="100" y="215" font-size="14" fill="#2c3e50" font-weight="bold">GC后：</text>
                <rect x="50" y="225" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="75" y="255" text-anchor="middle" fill="#fff" font-weight="bold">A</text>
                
                <rect x="110" y="225" width="50" height="50" fill="none" stroke="#f44336" stroke-width="2" stroke-dasharray="5,5"/>
                <text x="135" y="255" text-anchor="middle" fill="#f44336" font-size="14">❌</text>
                
                <rect x="170" y="225" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="195" y="255" text-anchor="middle" fill="#fff" font-weight="bold">C</text>
                
                <rect x="230" y="225" width="50" height="50" fill="none" stroke="#f44336" stroke-width="2" stroke-dasharray="5,5"/>
                <text x="255" y="255" text-anchor="middle" fill="#f44336" font-size="14">❌</text>
                
                <rect x="290" y="225" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="315" y="255" text-anchor="middle" fill="#fff" font-weight="bold">E</text>
                
                <!-- 说明 -->
                <rect x="380" y="80" width="270" height="195" fill="#fff3e0" stroke="#ff9800" stroke-width="2" rx="5"/>
                <text x="515" y="105" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">算法特点</text>
                <text x="390" y="135" font-size="11" fill="#2c3e50"><tspan font-weight="bold">✓ 优点：</tspan></text>
                <text x="400" y="155" font-size="10" fill="#666">• 实现简单</text>
                <text x="400" y="170" font-size="10" fill="#666">• 不需要额外空间</text>
                <text x="390" y="195" font-size="11" fill="#2c3e50"><tspan font-weight="bold">✗ 缺点：</tspan></text>
                <text x="400" y="215" font-size="10" fill="#f44336">• 产生内存碎片</text>
                <text x="400" y="230" font-size="10" fill="#f44336">• 两次遍历（标记+清除）</text>
                <text x="400" y="245" font-size="10" fill="#f44336">• 效率较低</text>
                <text x="515" y="265" text-anchor="middle" font-size="10" fill="#666" font-style="italic">适用：老年代（CMS GC）</text>
            `;
            
            document.getElementById('gcExplanation').innerHTML = `
                <strong>🔍 标记-清除算法</strong><br>
                步骤1：标记所有存活对象（A、C、E有引用）<br>
                步骤2：清除未标记对象（B、D被回收）<br>
                <em style="color: #f44336;">注意：会产生内存碎片，影响大对象分配</em>
            `;
        }
        
        function showCopyGC() {
            const svg = document.querySelector('#gcCanvas svg');
            svg.innerHTML = `
                <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">复制算法（Copying）</text>
                
                <!-- From区 -->
                <text x="120" y="70" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">From区</text>
                <rect x="50" y="80" width="140" height="100" fill="#ffebee" stroke="#e57373" stroke-width="3" rx="5"/>
                <rect x="60" y="95" width="35" height="35" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="77" y="118" text-anchor="middle" fill="#fff" font-weight="bold">A</text>
                <rect x="105" y="95" width="35" height="35" fill="#e0e0e0" stroke="#9e9e9e" stroke-width="2"/>
                <text x="122" y="118" text-anchor="middle" fill="#666">B</text>
                <rect x="150" y="95" width="35" height="35" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="167" y="118" text-anchor="middle" fill="#fff" font-weight="bold">C</text>
                <rect x="60" y="140" width="35" height="35" fill="#e0e0e0" stroke="#9e9e9e" stroke-width="2"/>
                <text x="77" y="163" text-anchor="middle" fill="#666">D</text>
                <rect x="105" y="140" width="35" height="35" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="122" y="163" text-anchor="middle" fill="#fff" font-weight="bold">E</text>
                
                <!-- 箭头 -->
                <path d="M 200 130 L 250 130" stroke="#03a9f4" stroke-width="3" fill="none" marker-end="url(#copyArrow)"/>
                <text x="225" y="125" text-anchor="middle" font-size="11" fill="#03a9f4" font-weight="bold">复制</text>
                <defs>
                    <marker id="copyArrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#03a9f4" />
                    </marker>
                </defs>
                
                <!-- To区 -->
                <text x="320" y="70" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">To区（空闲）</text>
                <rect x="250" y="80" width="140" height="100" fill="#e8f5e9" stroke="#66bb6a" stroke-width="3" rx="5"/>
                <rect x="260" y="95" width="35" height="35" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="277" y="118" text-anchor="middle" fill="#fff" font-weight="bold">A</text>
                <rect x="305" y="95" width="35" height="35" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="322" y="118" text-anchor="middle" fill="#fff" font-weight="bold">C</text>
                <rect x="350" y="95" width="35" height="35" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="367" y="118" text-anchor="middle" fill="#fff" font-weight="bold">E</text>
                
                <!-- GC后交换 -->
                <text x="200" y="210" text-anchor="middle" font-size="12" fill="#e91e63" font-weight="bold">GC后：From和To角色交换</text>
                <path d="M 120 225 Q 200 240 280 225" stroke="#e91e63" stroke-width="2" fill="none" marker-end="url(#swapArrow)" stroke-dasharray="5,5"/>
                <path d="M 320 225 Q 240 240 160 225" stroke="#e91e63" stroke-width="2" fill="none" marker-end="url(#swapArrow)" stroke-dasharray="5,5"/>
                <defs>
                    <marker id="swapArrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#e91e63" />
                    </marker>
                </defs>
                
                <!-- 说明 -->
                <rect x="420" y="80" width="230" height="170" fill="#e3f2fd" stroke="#2196f3" stroke-width="2" rx="5"/>
                <text x="535" y="105" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">算法特点</text>
                <text x="430" y="130" font-size="11" fill="#2c3e50"><tspan font-weight="bold">✓ 优点：</tspan></text>
                <text x="440" y="150" font-size="10" fill="#666">• 无内存碎片</text>
                <text x="440" y="165" font-size="10" fill="#666">• 分配快速</text>
                <text x="430" y="185" font-size="11" fill="#2c3e50"><tspan font-weight="bold">✗ 缺点：</tspan></text>
                <text x="440" y="205" font-size="10" fill="#f44336">• 浪费50%空间</text>
                <text x="440" y="220" font-size="10" fill="#f44336">• 存活对象多时效率低</text>
                <text x="535" y="240" text-anchor="middle" font-size="10" fill="#666" font-style="italic">适用：新生代（90%对象朝生夕死）</text>
            `;
            
            document.getElementById('gcExplanation').innerHTML = `
                <strong>📋 复制算法</strong><br>
                步骤1：将存活对象（A、C、E）从From区复制到To区<br>
                步骤2：清空From区<br>
                步骤3：交换From和To区角色<br>
                <em style="color: #4caf50;">优点：无碎片，适合新生代（对象存活率低）</em>
            `;
        }
        
        function showMarkCompact() {
            const svg = document.querySelector('#gcCanvas svg');
            svg.innerHTML = `
                <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">标记-整理算法（Mark-Compact）</text>
                
                <!-- GC前 -->
                <text x="100" y="70" font-size="14" fill="#2c3e50" font-weight="bold">GC前：</text>
                <rect x="50" y="80" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="75" y="110" text-anchor="middle" fill="#fff" font-weight="bold">A</text>
                <rect x="110" y="80" width="50" height="50" fill="#e0e0e0" stroke="#9e9e9e" stroke-width="2"/>
                <text x="135" y="110" text-anchor="middle" fill="#666">B</text>
                <rect x="170" y="80" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="195" y="110" text-anchor="middle" fill="#fff" font-weight="bold">C</text>
                <rect x="230" y="80" width="50" height="50" fill="#e0e0e0" stroke="#9e9e9e" stroke-width="2"/>
                <text x="255" y="110" text-anchor="middle" fill="#666">D</text>
                <rect x="290" y="80" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="315" y="110" text-anchor="middle" fill="#fff" font-weight="bold">E</text>
                
                <!-- 整理箭头 -->
                <path d="M 195 140 L 135 165" stroke="#9c27b0" stroke-width="2" fill="none" marker-end="url(#compactArrow)" stroke-dasharray="5,5"/>
                <path d="M 315 140 L 195 165" stroke="#9c27b0" stroke-width="2" fill="none" marker-end="url(#compactArrow)" stroke-dasharray="5,5"/>
                <text x="220" y="165" text-anchor="middle" font-size="12" fill="#9c27b0" font-weight="bold">← 整理压缩</text>
                <defs>
                    <marker id="compactArrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#9c27b0" />
                    </marker>
                </defs>
                
                <!-- GC后 -->
                <text x="100" y="200" font-size="14" fill="#2c3e50" font-weight="bold">GC后：</text>
                <rect x="50" y="210" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="75" y="240" text-anchor="middle" fill="#fff" font-weight="bold">A</text>
                <rect x="110" y="210" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="135" y="240" text-anchor="middle" fill="#fff" font-weight="bold">C</text>
                <rect x="170" y="210" width="50" height="50" fill="#4caf50" stroke="#388e3c" stroke-width="2"/>
                <text x="195" y="240" text-anchor="middle" fill="#fff" font-weight="bold">E</text>
                <rect x="230" y="210" width="110" height="50" fill="none" stroke="#4caf50" stroke-width="2" stroke-dasharray="5,5"/>
                <text x="285" y="240" text-anchor="middle" fill="#4caf50" font-size="14" font-weight="bold">连续空闲空间</text>
                
                <!-- 说明 -->
                <rect x="380" y="80" width="270" height="180" fill="#f3e5f5" stroke="#9c27b0" stroke-width="2" rx="5"/>
                <text x="515" y="105" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">算法特点</text>
                <text x="390" y="130" font-size="11" fill="#2c3e50"><tspan font-weight="bold">✓ 优点：</tspan></text>
                <text x="400" y="150" font-size="10" fill="#666">• 无内存碎片</text>
                <text x="400" y="165" font-size="10" fill="#666">• 不浪费空间</text>
                <text x="400" y="180" font-size="10" fill="#666">• 存活对象多时高效</text>
                <text x="390" y="200" font-size="11" fill="#2c3e50"><tspan font-weight="bold">✗ 缺点：</tspan></text>
                <text x="400" y="220" font-size="10" fill="#f44336">• 需要移动对象（更新引用）</text>
                <text x="400" y="235" font-size="10" fill="#f44336">• 需要暂停应用（STW）</text>
                <text x="515" y="255" text-anchor="middle" font-size="10" fill="#666" font-style="italic">适用：老年代（G1 GC）</text>
            `;
            
            document.getElementById('gcExplanation').innerHTML = `
                <strong>🎯 标记-整理算法</strong><br>
                步骤1：标记所有存活对象（A、C、E）<br>
                步骤2：将存活对象移动到内存一端，紧密排列<br>
                步骤3：清除边界外的所有内存<br>
                <em style="color: #9c27b0;">优点：既无碎片又不浪费空间，适合老年代</em>
            `;
        }
        
        function resetGCView() {
            const svg = document.querySelector('#gcCanvas svg');
            svg.innerHTML = `
                <text x="350" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">GC算法对比</text>
                <text x="350" y="150" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看不同GC算法的工作过程</text>
            `;
            
            document.getElementById('gcExplanation').innerHTML = '选择一个GC算法查看详细演示';
        }
        
        // ==================== 线程池可视化 ====================
        function showThreadPoolFlow() {
            const svg = document.querySelector('#threadPoolCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">线程池执行流程</text>
                
                <!-- 任务到达 -->
                <rect x="50" y="50" width="120" height="280" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="110" y="75" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">任务队列</text>
                <circle cx="110" cy="100" r="18" fill="#ff9800" stroke="#f57c00" stroke-width="2"/>
                <text x="110" y="106" text-anchor="middle" fill="white" font-size="14" font-weight="bold">T1</text>
                <circle cx="110" cy="140" r="18" fill="#ff9800" stroke="#f57c00" stroke-width="2"/>
                <text x="110" y="146" text-anchor="middle" fill="white" font-size="14" font-weight="bold">T2</text>
                <circle cx="110" cy="180" r="18" fill="#ff9800" stroke="#f57c00" stroke-width="2"/>
                <text x="110" y="186" text-anchor="middle" fill="white" font-size="14" font-weight="bold">T3</text>
                <circle cx="110" cy="220" r="18" fill="#ff9800" stroke="#f57c00" stroke-width="2"/>
                <text x="110" y="226" text-anchor="middle" fill="white" font-size="14" font-weight="bold">T4</text>
                <text x="110" y="260" text-anchor="middle" font-size="11" fill="#666">队列容量: 10</text>
                <text x="110" y="280" text-anchor="middle" font-size="11" fill="#2196f3" font-weight="bold">已使用: 4</text>
                
                <!-- 核心线程池 -->
                <rect x="220" y="50" width="180" height="140" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="310" y="75" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">核心线程池</text>
                <text x="310" y="90" text-anchor="middle" font-size="11" fill="#666">corePoolSize = 3</text>
                <rect x="235" y="100" width="60" height="40" fill="#81c784" stroke="#388e3c" stroke-width="2" rx="4"/>
                <text x="265" y="118" text-anchor="middle" font-size="12" fill="white" font-weight="bold">线程1</text>
                <text x="265" y="132" text-anchor="middle" font-size="10" fill="white">执行中</text>
                <rect x="310" y="100" width="60" height="40" fill="#81c784" stroke="#388e3c" stroke-width="2" rx="4"/>
                <text x="340" y="118" text-anchor="middle" font-size="12" fill="white" font-weight="bold">线程2</text>
                <text x="340" y="132" text-anchor="middle" font-size="10" fill="white">执行中</text>
                <rect x="235" y="150" width="60" height="40" fill="#81c784" stroke="#388e3c" stroke-width="2" rx="4"/>
                <text x="265" y="168" text-anchor="middle" font-size="12" fill="white" font-weight="bold">线程3</text>
                <text x="265" y="182" text-anchor="middle" font-size="10" fill="white">执行中</text>
                
                <!-- 非核心线程池 -->
                <rect x="220" y="210" width="180" height="120" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="310" y="235" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">非核心线程</text>
                <text x="310" y="250" text-anchor="middle" font-size="11" fill="#666">maxPoolSize = 5</text>
                <rect x="235" y="260" width="60" height="40" fill="#ffb74d" stroke="#f57c00" stroke-width="2" rx="4"/>
                <text x="265" y="278" text-anchor="middle" font-size="12" fill="white" font-weight="bold">线程4</text>
                <text x="265" y="292" text-anchor="middle" font-size="10" fill="white">空闲</text>
                <rect x="310" y="260" width="60" height="40" fill="#ffb74d" stroke="#f57c00" stroke-width="2" rx="4"/>
                <text x="340" y="278" text-anchor="middle" font-size="12" fill="white" font-weight="bold">线程5</text>
                <text x="340" y="292" text-anchor="middle" font-size="10" fill="white">空闲</text>
                
                <!-- 执行流程箭头 -->
                <defs>
                    <marker id="flowArrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
                        <polygon points="0 0, 10 3, 0 6" fill="#e91e63" />
                    </marker>
                </defs>
                
                <!-- 新任务 -->
                <circle cx="550" cy="100" r="22" fill="#e74c3c" stroke="#c0392b" stroke-width="3"/>
                <text x="550" y="106" text-anchor="middle" fill="white" font-size="14" font-weight="bold">新任务</text>
                
                <!-- 流程说明 -->
                <text x="450" y="150" font-size="11" fill="#2c3e50">1️⃣ 线程数 < corePoolSize</text>
                <path d="M 528 100 L 410 120" stroke="#e91e63" stroke-width="2" fill="none" marker-end="url(#flowArrow)"/>
                <text x="470" y="165" font-size="11" fill="#e91e63" font-weight="bold">→ 创建核心线程</text>
                
                <text x="450" y="200" font-size="11" fill="#2c3e50">2️⃣ 线程数 = corePoolSize</text>
                <path d="M 528 100 L 180 160" stroke="#e91e63" stroke-width="2" fill="none" marker-end="url(#flowArrow)" stroke-dasharray="5,5"/>
                <text x="350" y="145" font-size="11" fill="#e91e63" font-weight="bold">→ 任务入队列</text>
                
                <text x="450" y="250" font-size="11" fill="#2c3e50">3️⃣ 队列满 && 线程数 < max</text>
                <path d="M 528 100 L 410 280" stroke="#e91e63" stroke-width="2" fill="none" marker-end="url(#flowArrow)" stroke-dasharray="3,3"/>
                <text x="470" y="290" font-size="11" fill="#e91e63" font-weight="bold">→ 创建非核心线程</text>
                
                <text x="550" y="330" text-anchor="middle" font-size="11" fill="#f44336" font-weight="bold">4️⃣ 队列满 && 线程数 = max → 执行拒绝策略</text>
            `;
            
            document.getElementById('threadPoolExplanation').innerHTML = `
                <strong>🔄 线程池执行流程</strong><br>
                当前状态：核心线程3个全部忙碌，队列已有4个任务等待<br>
                <span style="color: #4caf50;">✓ 核心线程：</span>始终保持活跃<br>
                <span style="color: #ff9800;">⚠️ 非核心线程：</span>空闲超过keepAliveTime会销毁
            `;
        }
        
        function showRejectionPolicy() {
            const svg = document.querySelector('#threadPoolCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">四种拒绝策略对比</text>
                
                <!-- AbortPolicy -->
                <rect x="50" y="60" width="160" height="140" fill="#ffebee" stroke="#f44336" stroke-width="3" rx="5"/>
                <text x="130" y="85" text-anchor="middle" font-size="14" fill="#f44336" font-weight="bold">AbortPolicy</text>
                <text x="130" y="105" text-anchor="middle" font-size="11" fill="#666">（默认策略）</text>
                <text x="60" y="130" font-size="11" fill="#2c3e50">❌ 抛出异常</text>
                <text x="60" y="150" font-size="11" fill="#2c3e50">❌ 任务丢失</text>
                <text x="60" y="170" font-size="11" fill="#f44336">需要处理异常</text>
                <text x="130" y="190" text-anchor="middle" font-size="10" fill="#666" font-style="italic">适合：不能丢失任务</text>
                
                <!-- CallerRunsPolicy -->
                <rect x="230" y="60" width="160" height="140" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="5"/>
                <text x="310" y="85" text-anchor="middle" font-size="14" fill="#ff9800" font-weight="bold">CallerRuns</text>
                <text x="310" y="105" text-anchor="middle" font-size="11" fill="#666">（降级策略）</text>
                <text x="240" y="130" font-size="11" fill="#2c3e50">✓ 调用者执行</text>
                <text x="240" y="150" font-size="11" fill="#2c3e50">✓ 不抛异常</text>
                <text x="240" y="170" font-size="11" fill="#ff9800">降低吞吐量</text>
                <text x="310" y="190" text-anchor="middle" font-size="10" fill="#666" font-style="italic">适合：允许降速</text>
                
                <!-- DiscardPolicy -->
                <rect x="50" y="220" width="160" height="140" fill="#f3e5f5" stroke="#9c27b0" stroke-width="3" rx="5"/>
                <text x="130" y="245" text-anchor="middle" font-size="14" fill="#9c27b0" font-weight="bold">DiscardPolicy</text>
                <text x="130" y="265" text-anchor="middle" font-size="11" fill="#666">（丢弃策略）</text>
                <text x="60" y="290" font-size="11" fill="#2c3e50">✓ 直接丢弃</text>
                <text x="60" y="310" font-size="11" fill="#2c3e50">✓ 不抛异常</text>
                <text x="60" y="330" font-size="11" fill="#9c27b0">任务永久丢失</text>
                <text x="130" y="350" text-anchor="middle" font-size="10" fill="#666" font-style="italic">适合：可丢弃任务</text>
                
                <!-- DiscardOldestPolicy -->
                <rect x="230" y="220" width="160" height="140" fill="#e0f2f1" stroke="#009688" stroke-width="3" rx="5"/>
                <text x="310" y="245" text-anchor="middle" font-size="13" fill="#009688" font-weight="bold">DiscardOldest</text>
                <text x="310" y="265" text-anchor="middle" font-size="11" fill="#666">（丢最旧）</text>
                <text x="240" y="290" font-size="11" fill="#2c3e50">✓ 丢队首任务</text>
                <text x="240" y="310" font-size="11" fill="#2c3e50">✓ 加入新任务</text>
                <text x="240" y="330" font-size="11" fill="#009688">保留最新任务</text>
                <text x="310" y="350" text-anchor="middle" font-size="10" fill="#666" font-style="italic">适合：新任务重要</text>
                
                <!-- 对比说明 -->
                <rect x="420" y="60" width="330" height="300" fill="#fafafa" stroke="#bdbdbd" stroke-width="2" rx="5"/>
                <text x="585" y="85" text-anchor="middle" font-size="15" fill="#2c3e50" font-weight="bold">选择建议</text>
                
                <text x="435" y="115" font-size="12" fill="#2c3e50" font-weight="bold">🎯 生产环境推荐：</text>
                <text x="445" y="135" font-size="11" fill="#666">1. 优先：<span style="color:#ff9800;font-weight:bold">CallerRunsPolicy</span></text>
                <text x="455" y="155" font-size="10" fill="#666">• 不丢失任务</text>
                <text x="455" y="170" font-size="10" fill="#666">• 提供背压（反向降速）</text>
                
                <text x="445" y="195" font-size="11" fill="#666">2. 其次：<span style="color:#f44336;font-weight:bold">AbortPolicy</span></text>
                <text x="455" y="215" font-size="10" fill="#666">• 快速失败，便于排查</text>
                <text x="455" y="230" font-size="10" fill="#666">• 需要处理异常</text>
                
                <text x="445" y="255" font-size="11" fill="#666">3. 谨慎：<span style="color:#9c27b0">Discard策略</span></text>
                <text x="455" y="275" font-size="10" fill="#666">• 会丢失任务，难以追踪</text>
                <text x="455" y="290" font-size="10" fill="#666">• 仅用于可丢弃场景</text>
                
                <text x="585" y="320" text-anchor="middle" font-size="11" fill="#f44336" font-weight="bold">⚠️ 关键：根据业务场景选择</text>
                <text x="585" y="340" text-anchor="middle" font-size="10" fill="#666">监控拒绝数量，及时调整线程池参数</text>
            `;
            
            document.getElementById('threadPoolExplanation').innerHTML = `
                <strong>🚫 拒绝策略对比</strong><br>
                四种策略各有适用场景，需根据业务重要性和系统负载选择<br>
                <em style="color: #ff9800;">推荐：CallerRunsPolicy（提供背压机制）</em>
            `;
        }
        
        function resetThreadPoolView() {
            const svg = document.querySelector('#threadPoolCanvas svg');
            svg.innerHTML = `
                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">线程池工作原理</text>
                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细流程</text>
            `;
            
            document.getElementById('threadPoolExplanation').innerHTML = '点击按钮查看线程池工作流程或拒绝策略';
        }
        
        // ==================== Flink可视化 ====================
        function showFlinkArchitecture() {
            const svg = document.querySelector('#flinkCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Flink架构</text>
                
                <!-- JobManager -->
                <rect x="100" y="60" width="200" height="100" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="200" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">JobManager</text>
                <text x="110" y="110" font-size="11" fill="#666">• 调度任务</text>
                <text x="110" y="130" font-size="11" fill="#666">• 管理Checkpoint</text>
                <text x="110" y="150" font-size="11" fill="#666">• 协调任务执行</text>
                
                <!-- TaskManager 1 -->
                <rect x="380" y="60" width="150" height="100" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="455" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">TaskManager 1</text>
                <text x="390" y="110" font-size="10" fill="#666">Slot1: Source</text>
                <text x="390" y="130" font-size="10" fill="#666">Slot2: Transform</text>
                <text x="390" y="150" font-size="10" fill="#666">Slot3: Sink</text>
                
                <!-- TaskManager 2 -->
                <rect x="560" y="60" width="150" height="100" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="635" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">TaskManager 2</text>
                <text x="570" y="110" font-size="10" fill="#666">Slot1: Source</text>
                <text x="570" y="130" font-size="10" fill="#666">Slot2: Transform</text>
                <text x="570" y="150" font-size="10" fill="#666">Slot3: Sink</text>
                
                <!-- 数据流 -->
                <defs>
                    <marker id="arrowblue" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto">
                        <polygon points="0 0, 8 3, 0 6" fill="#2196f3" />
                    </marker>
                </defs>
                
                <!-- 箭头 -->
                <path d="M 300 110 L 370 110" stroke="#2196f3" stroke-width="2" fill="none" marker-end="url(#arrowblue)"/>
                <text x="330" y="100" font-size="10" fill="#2196f3" font-weight="bold">分配任务</text>
                
                <path d="M 300 130 L 550 110" stroke="#2196f3" stroke-width="2" fill="none" marker-end="url(#arrowblue)"/>
                
                <!-- 数据源和数据汇 -->
                <rect x="50" y="220" width="140" height="60" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="120" y="245" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">数据源</text>
                <text x="120" y="265" text-anchor="middle" font-size="11" fill="#666">Kafka/MySQL/...</text>
                
                <rect x="620" y="220" width="140" height="60" fill="#f3e5f5" stroke="#9c27b0" stroke-width="3" rx="8"/>
                <text x="690" y="245" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">数据汇</text>
                <text x="690" y="265" text-anchor="middle" font-size="11" fill="#666">HDFS/ES/...</text>
                
                <!-- 数据流向 -->
                <path d="M 190 250 L 370 140" stroke="#ff6b6b" stroke-width="2" fill="none" marker-end="url(#arrowblue)" stroke-dasharray="5,5"/>
                <path d="M 540 140 L 620 250" stroke="#ff6b6b" stroke-width="2" fill="none" marker-end="url(#arrowblue)" stroke-dasharray="5,5"/>
                
                <!-- HDFS存储 -->
                <rect x="250" y="320" width="300" height="60" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="8"/>
                <text x="400" y="345" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">HDFS / S3</text>
                <text x="400" y="365" text-anchor="middle" font-size="11" fill="#666">存储Checkpoint快照</text>
                
                <path d="M 200 160 L 400 310" stroke="#03a9f4" stroke-width="2" fill="none" marker-end="url(#arrowblue)" stroke-dasharray="3,3"/>
                <text x="280" y="230" font-size="10" fill="#03a9f4" font-weight="bold">保存状态</text>
            `;
            
            document.getElementById('flinkExplanation').innerHTML = `
                <strong>🏗️ Flink架构</strong><br>
                <span style="color: #2196f3;">JobManager：</span>负责任务调度和Checkpoint协调<br>
                <span style="color: #4caf50;">TaskManager：</span>实际执行任务的工作节点<br>
                <span style="color: #03a9f4;">HDFS/S3：</span>持久化存储Checkpoint状态
            `;
        }
        
        function showCheckpointFlow() {
            const svg = document.querySelector('#flinkCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Checkpoint流程</text>
                
                <!-- 步骤1：JobManager触发 -->
                <rect x="50" y="60" width="180" height="60" fill="#ffebee" stroke="#f44336" stroke-width="3" rx="8"/>
                <text x="140" y="80" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">1️⃣ JobManager触发</text>
                <text x="60" y="105" font-size="10" fill="#666">定期触发Checkpoint</text>
                <text x="60" y="120" font-size="10" fill="#666">生成Barrier标记</text>
                
                <!-- 步骤2：Source接收Barrier -->
                <rect x="50" y="140" width="180" height="60" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="140" y="160" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">2️⃣ Source接收Barrier</text>
                <text x="60" y="185" font-size="10" fill="#666">在数据流中插入Barrier</text>
                
                <!-- 步骤3：算子保存状态 -->
                <rect x="260" y="60" width="240" height="140" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="380" y="80" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">3️⃣ 算子保存状态</text>
                <text x="270" y="105" font-size="10" fill="#666">• Source算子：保存offset</text>
                <text x="270" y="125" font-size="10" fill="#666">• Transform算子：保存中间状态</text>
                <text x="270" y="145" font-size="10" fill="#666">• Sink算子：保存写入进度</text>
                <text x="270" y="165" font-size="10" fill="#666">• Barrier对齐：等待所有输入</text>
                <text x="270" y="185" font-size="10" fill="#666">• 异步上传到HDFS/S3</text>
                
                <!-- 步骤4：确认完成 -->
                <rect x="530" y="60" width="220" height="60" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="8"/>
                <text x="640" y="80" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">4️⃣ 确认完成</text>
                <text x="540" y="105" font-size="10" fill="#666">所有算子都完成后</text>
                <text x="540" y="120" font-size="10" fill="#666">标记Checkpoint成功</text>
                
                <!-- 步骤5：故障恢复 -->
                <rect x="530" y="140" width="220" height="60" fill="#f3e5f5" stroke="#9c27b0" stroke-width="3" rx="8"/>
                <text x="640" y="160" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">5️⃣ 故障恢复</text>
                <text x="540" y="185" font-size="10" fill="#666">任务失败时从最近的</text>
                <text x="540" y="200" font-size="10" fill="#666">Checkpoint恢复</text>
                
                <!-- 流程箭头 -->
                <defs>
                    <marker id="arrowgreen" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto">
                        <polygon points="0 0, 8 3, 0 6" fill="#4caf50" />
                    </marker>
                </defs>
                
                <path d="M 140 120 L 140 135" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowgreen)"/>
                <path d="M 230 170 L 255 110" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowgreen)"/>
                <path d="M 500 110 L 525 90" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowgreen)"/>
                <path d="M 640 120 L 640 135" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowgreen)"/>
                
                <!-- 说明 -->
                <rect x="50" y="230" width="700" height="150" fill="#fafafa" stroke="#bdbdbd" stroke-width="2" rx="5"/>
                <text x="400" y="255" text-anchor="middle" font-size="15" fill="#2c3e50" font-weight="bold">Checkpoint关键特性</text>
                
                <text x="70" y="285" font-size="12" fill="#2c3e50" font-weight="bold">✓ Exactly-Once语义</text>
                <text x="80" y="305" font-size="10" fill="#666">通过Barrier对齐确保数据不重不丢</text>
                
                <text x="300" y="285" font-size="12" fill="#2c3e50" font-weight="bold">✓ 异步非阻塞</text>
                <text x="310" y="305" font-size="10" fill="#666">状态快照异步上传，不阻塞数据流</text>
                
                <text x="530" y="285" font-size="12" fill="#2c3e50" font-weight="bold">✓ 增量快照</text>
                <text x="540" y="305" font-size="10" fill="#666">RocksDB增量Checkpoint节省时间</text>
                
                <text x="70" y="335" font-size="12" fill="#2c3e50" font-weight="bold">🎯 优化建议</text>
                <text x="80" y="355" font-size="10" fill="#666">• 间隔：5-10分钟（太频繁影响性能）</text>
                <text x="400" y="355" font-size="10" fill="#666">• 超时：10分钟（避免长时间阻塞）</text>
            `;
            
            document.getElementById('flinkExplanation').innerHTML = `
                <strong>💾 Checkpoint流程</strong><br>
                定期保存状态快照，通过Barrier机制保证Exactly-Once语义<br>
                <em style="color: #4caf50;">RocksDB增量Checkpoint大幅提升性能</em>
            `;
        }
        
        function resetFlinkView() {
            const svg = document.querySelector('#flinkCanvas svg');
            svg.innerHTML = `
                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Flink工作原理</text>
                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
            `;
            
            document.getElementById('flinkExplanation').innerHTML = '点击按钮查看Flink架构或Checkpoint流程';
        }
        
        // ==================== Kafka可视化 ====================
        function showKafkaArchitecture() {
            const svg = document.querySelector('#kafkaCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Kafka架构</text>
                
                <!-- Producer -->
                <rect x="50" y="60" width="130" height="280" fill="#ffebee" stroke="#f44336" stroke-width="3" rx="8"/>
                <text x="115" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">生产者</text>
                <circle cx="115" cy="120" r="20" fill="#ff6b6b" stroke="#f44336" stroke-width="2"/>
                <text x="115" y="126" text-anchor="middle" fill="white" font-size="12" font-weight="bold">P1</text>
                <circle cx="115" cy="170" r="20" fill="#ff6b6b" stroke="#f44336" stroke-width="2"/>
                <text x="115" y="176" text-anchor="middle" fill="white" font-size="12" font-weight="bold">P2</text>
                <circle cx="115" cy="220" r="20" fill="#ff6b6b" stroke="#f44336" stroke-width="2"/>
                <text x="115" y="226" text-anchor="middle" fill="white" font-size="12" font-weight="bold">P3</text>
                <text x="115" y="270" text-anchor="middle" font-size="11" fill="#666">发送消息</text>
                <text x="115" y="290" text-anchor="middle" font-size="11" fill="#666">到Topic</text>
                
                <!-- Kafka Cluster -->
                <rect x="220" y="60" width="360" height="280" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="400" y="85" text-anchor="middle" font-size="15" fill="#2c3e50" font-weight="bold">Kafka Cluster</text>
                
                <!-- Topic & Partitions -->
                <rect x="240" y="100" width="150" height="220" fill="#fff3e0" stroke="#ff9800" stroke-width="2" rx="5"/>
                <text x="315" y="120" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">Topic: 交易流水</text>
                
                <rect x="250" y="135" width="130" height="40" fill="#a5d6a7" stroke="#4caf50" stroke-width="1"/>
                <text x="315" y="153" text-anchor="middle" font-size="11" fill="#2c3e50">Partition 0</text>
                <text x="315" y="168" text-anchor="middle" font-size="9" fill="#666">[msg1, msg2, ...]</text>
                
                <rect x="250" y="185" width="130" height="40" fill="#a5d6a7" stroke="#4caf50" stroke-width="1"/>
                <text x="315" y="203" text-anchor="middle" font-size="11" fill="#2c3e50">Partition 1</text>
                <text x="315" y="218" text-anchor="middle" font-size="9" fill="#666">[msg3, msg4, ...]</text>
                
                <rect x="250" y="235" width="130" height="40" fill="#a5d6a7" stroke="#4caf50" stroke-width="1"/>
                <text x="315" y="253" text-anchor="middle" font-size="11" fill="#2c3e50">Partition 2</text>
                <text x="315" y="268" text-anchor="middle" font-size="9" fill="#666">[msg5, msg6, ...]</text>
                
                <rect x="250" y="285" width="130" height="30" fill="#e0e0e0" stroke="#9e9e9e" stroke-width="1"/>
                <text x="315" y="303" text-anchor="middle" font-size="10" fill="#666">...</text>
                
                <!-- Broker Info -->
                <rect x="410" y="100" width="150" height="220" fill="#e8f5e9" stroke="#4caf50" stroke-width="2" rx="5"/>
                <text x="485" y="120" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">Broker集群</text>
                
                <rect x="420" y="135" width="130" height="35" fill="#81c784" stroke="#388e3c" stroke-width="1"/>
                <text x="485" y="153" text-anchor="middle" font-size="11" fill="white" font-weight="bold">Broker 1</text>
                <text x="485" y="166" text-anchor="middle" font-size="9" fill="white">P0-Leader</text>
                
                <rect x="420" y="180" width="130" height="35" fill="#81c784" stroke="#388e3c" stroke-width="1"/>
                <text x="485" y="198" text-anchor="middle" font-size="11" fill="white" font-weight="bold">Broker 2</text>
                <text x="485" y="211" text-anchor="middle" font-size="9" fill="white">P1-Leader</text>
                
                <rect x="420" y="225" width="130" height="35" fill="#81c784" stroke="#388e3c" stroke-width="1"/>
                <text x="485" y="243" text-anchor="middle" font-size="11" fill="white" font-weight="bold">Broker 3</text>
                <text x="485" y="256" text-anchor="middle" font-size="9" fill="white">P2-Leader</text>
                
                <text x="485" y="285" text-anchor="middle" font-size="10" fill="#666">高可用：副本机制</text>
                <text x="485" y="305" text-anchor="middle" font-size="10" fill="#666">Leader + Follower</text>
                
                <!-- Consumer -->
                <rect x="620" y="60" width="130" height="280" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="685" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">消费者组</text>
                <circle cx="685" cy="120" r="20" fill="#66bb6a" stroke="#4caf50" stroke-width="2"/>
                <text x="685" y="126" text-anchor="middle" fill="white" font-size="12" font-weight="bold">C1</text>
                <text x="685" y="150" font-size="9" fill="#666" text-anchor="middle">消费P0</text>
                
                <circle cx="685" cy="180" r="20" fill="#66bb6a" stroke="#4caf50" stroke-width="2"/>
                <text x="685" y="186" text-anchor="middle" fill="white" font-size="12" font-weight="bold">C2</text>
                <text x="685" y="210" font-size="9" fill="#666" text-anchor="middle">消费P1</text>
                
                <circle cx="685" cy="240" r="20" fill="#66bb6a" stroke="#4caf50" stroke-width="2"/>
                <text x="685" y="246" text-anchor="middle" fill="white" font-size="12" font-weight="bold">C3</text>
                <text x="685" y="270" font-size="9" fill="#666" text-anchor="middle">消费P2</text>
                
                <text x="685" y="300" text-anchor="middle" font-size="11" fill="#666">订阅Topic</text>
                <text x="685" y="320" text-anchor="middle" font-size="11" fill="#666">消费消息</text>
                
                <!-- 数据流向箭头 -->
                <defs>
                    <marker id="arrowkafka" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto">
                        <polygon points="0 0, 8 3, 0 6" fill="#ff9800" />
                    </marker>
                </defs>
                
                <path d="M 135 120 L 240 155" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowkafka)"/>
                <path d="M 135 170 L 240 205" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowkafka)"/>
                <path d="M 135 220 L 240 255" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowkafka)"/>
                
                <path d="M 390 155 L 615 120" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowkafka)"/>
                <path d="M 390 205 L 615 180" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowkafka)"/>
                <path d="M 390 255 L 615 240" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowkafka)"/>
            `;
            
            document.getElementById('kafkaExplanation').innerHTML = `
                <strong>📊 Kafka架构</strong><br>
                <span style="color: #f44336;">生产者：</span>发送消息到Topic<br>
                <span style="color: #ff9800;">Topic：</span>分为多个Partition，实现并行处理<br>
                <span style="color: #4caf50;">消费者组：</span>每个消费者负责一个或多个Partition
            `;
        }
        
        function showKafkaPartition() {
            const svg = document.querySelector('#kafkaCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Kafka分区机制</text>
                
                <!-- 消息分发 -->
                <rect x="50" y="60" width="180" height="280" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="140" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">消息分发策略</text>
                
                <text x="60" y="115" font-size="12" fill="#2c3e50" font-weight="bold">1. 指定Partition</text>
                <text x="70" y="135" font-size="10" fill="#666">直接发到指定分区</text>
                
                <text x="60" y="165" font-size="12" fill="#2c3e50" font-weight="bold">2. 有Key</text>
                <text x="70" y="185" font-size="10" fill="#666">hash(key) % partition数</text>
                <text x="70" y="202" font-size="10" fill="#666">相同key→同一分区</text>
                <text x="70" y="219" font-size="10" fill="#666">保证顺序性</text>
                
                <text x="60" y="249" font-size="12" fill="#2c3e50" font-weight="bold">3. 无Key</text>
                <text x="70" y="269" font-size="10" fill="#666">轮询（Round-Robin）</text>
                <text x="70" y="286" font-size="10" fill="#666">均匀分配</text>
                <text x="70" y="303" font-size="10" fill="#666">负载均衡</text>
                
                <!-- 分区好处 -->
                <rect x="260" y="60" width="240" height="280" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="380" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">分区的好处</text>
                
                <text x="270" y="115" font-size="12" fill="#2c3e50" font-weight="bold">✓ 并行处理</text>
                <text x="280" y="135" font-size="10" fill="#666">多个分区=多个消费者</text>
                <text x="280" y="152" font-size="10" fill="#666">吞吐量线性提升</text>
                
                <text x="270" y="182" font-size="12" fill="#2c3e50" font-weight="bold">✓ 负载均衡</text>
                <text x="280" y="202" font-size="10" fill="#666">数据均匀分布</text>
                <text x="280" y="219" font-size="10" fill="#666">避免单点热点</text>
                
                <text x="270" y="249" font-size="12" fill="#2c3e50" font-weight="bold">✓ 顺序保证</text>
                <text x="280" y="269" font-size="10" fill="#666">同一分区内有序</text>
                <text x="280" y="286" font-size="10" fill="#666">相同Key→同一分区</text>
                <text x="280" y="303" font-size="10" fill="#666">保证事件顺序</text>
                
                <!-- 优化建议 -->
                <rect x="530" y="60" width="220" height="280" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="640" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">分区数优化</text>
                
                <text x="540" y="115" font-size="12" fill="#2c3e50" font-weight="bold">🎯 设置原则</text>
                <text x="550" y="135" font-size="10" fill="#666">分区数 ≥ 消费者数</text>
                <text x="550" y="152" font-size="10" fill="#666">过多：增加开销</text>
                <text x="550" y="169" font-size="10" fill="#666">过少：影响并行度</text>
                
                <text x="540" y="199" font-size="12" fill="#2c3e50" font-weight="bold">📈 项目实践</text>
                <text x="550" y="219" font-size="10" fill="#666">核心Topic：256分区</text>
                <text x="550" y="236" font-size="10" fill="#666">配合Flink 128并行度</text>
                <text x="550" y="253" font-size="10" fill="#666">吞吐量：百万级QPS</text>
                
                <text x="540" y="283" font-size="12" fill="#2c3e50" font-weight="bold">⚠️ 注意事项</text>
                <text x="550" y="303" font-size="10" fill="#666">分区数不要动态改变</text>
                <text x="550" y="320" font-size="10" fill="#666">影响Key分布</text>
            `;
            
            document.getElementById('kafkaExplanation').innerHTML = `
                <strong>📦 Kafka分区机制</strong><br>
                分区是Kafka并行处理的核心，实现高吞吐和负载均衡<br>
                <em style="color: #ff9800;">项目中：256分区配合128并行度，达到百万级QPS</em>
            `;
        }
        
        function resetKafkaView() {
            const svg = document.querySelector('#kafkaCanvas svg');
            svg.innerHTML = `
                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Kafka工作原理</text>
                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
            `;
            
            document.getElementById('kafkaExplanation').innerHTML = '点击按钮查看Kafka架构或分区机制';
        }
        
        // ==================== RAG可视化 ====================
        function showRAGFlow() {
            const svg = document.querySelector('#ragCanvas svg');
            svg.innerHTML = `
                <text x="450" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">RAG完整流程</text>
                
                <!-- 步骤1：文档处理 -->
                <rect x="50" y="60" width="150" height="90" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="125" y="80" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">1️⃣ 文档处理</text>
                <text x="60" y="105" font-size="10" fill="#666">• PDF/Word解析</text>
                <text x="60" y="123" font-size="10" fill="#666">• 智能切片</text>
                <text x="60" y="141" font-size="10" fill="#666">• 500-1000字/段</text>
                
                <!-- 步骤2：向量化 -->
                <rect x="230" y="60" width="150" height="90" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="305" y="80" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">2️⃣ 向量化</text>
                <text x="240" y="105" font-size="10" fill="#666">• BGE-M3模型</text>
                <text x="240" y="123" font-size="10" fill="#666">• 1024维向量</text>
                <text x="240" y="141" font-size="10" fill="#666">• 语义编码</text>
                
                <!-- 步骤3：存储 -->
                <rect x="410" y="60" width="150" height="90" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="485" y="80" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">3️⃣ 向量存储</text>
                <text x="420" y="105" font-size="10" fill="#666">• Milvus数据库</text>
                <text x="420" y="123" font-size="10" fill="#666">• HNSW索引</text>
                <text x="420" y="141" font-size="10" fill="#666">• 百万级文档</text>
                
                <!-- 步骤4：用户查询 -->
                <rect x="590" y="60" width="150" height="90" fill="#ffebee" stroke="#f44336" stroke-width="3" rx="8"/>
                <text x="665" y="80" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">4️⃣ 用户查询</text>
                <text x="600" y="105" font-size="10" fill="#666">"Flink怎么部署？"</text>
                <text x="600" y="123" font-size="10" fill="#666">查询向量化</text>
                <text x="600" y="141" font-size="10" fill="#666">[0.2, 0.8, ...]</text>
                
                <!-- 步骤5：混合检索 -->
                <rect x="590" y="180" width="150" height="110" fill="#f3e5f5" stroke="#9c27b0" stroke-width="3" rx="8"/>
                <text x="665" y="200" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">5️⃣ 混合检索</text>
                <text x="600" y="225" font-size="10" fill="#666">• 向量检索Top20</text>
                <text x="600" y="243" font-size="10" fill="#666">• BM25关键词</text>
                <text x="600" y="261" font-size="10" fill="#666">• 混合召回</text>
                <text x="600" y="279" font-size="10" fill="#666">• 延迟<50ms</text>
                
                <!-- 步骤6：重排序 -->
                <rect x="410" y="180" width="150" height="110" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="8"/>
                <text x="485" y="200" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">6️⃣ 重排序</text>
                <text x="420" y="225" font-size="10" fill="#666">• gte-Qwen2-7B</text>
                <text x="420" y="243" font-size="10" fill="#666">• 精准打分</text>
                <text x="420" y="261" font-size="10" fill="#666">• 选Top3-5</text>
                <text x="420" y="279" font-size="10" fill="#666">• 最相关文档</text>
                
                <!-- 步骤7：生成答案 -->
                <rect x="230" y="180" width="150" height="110" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="305" y="200" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">7️⃣ 生成答案</text>
                <text x="240" y="225" font-size="10" fill="#666">• Qwen2.5-14B</text>
                <text x="240" y="243" font-size="10" fill="#666">• 基于文档回答</text>
                <text x="240" y="261" font-size="10" fill="#666">• 标注来源</text>
                <text x="240" y="279" font-size="10" fill="#666">• 可追溯</text>
                
                <!-- 步骤8：返回结果 -->
                <rect x="50" y="180" width="150" height="110" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="125" y="200" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">8️⃣ 返回用户</text>
                <text x="60" y="225" font-size="10" fill="#666">答案 + 来源文档</text>
                <text x="60" y="243" font-size="10" fill="#666">支持追溯验证</text>
                <text x="60" y="261" font-size="10" fill="#666">提升可信度</text>
                
                <!-- 流程箭头 -->
                <defs>
                    <marker id="arrowrag" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto">
                        <polygon points="0 0, 8 3, 0 6" fill="#ff9800" />
                    </marker>
                </defs>
                
                <path d="M 200 105 L 225 105" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowrag)"/>
                <path d="M 380 105 L 405 105" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowrag)"/>
                <path d="M 560 105 L 585 105" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowrag)"/>
                
                <path d="M 665 150 L 665 175" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowrag)"/>
                <path d="M 585 235 L 565 235" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowrag)"/>
                <path d="M 405 235 L 385 235" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowrag)"/>
                <path d="M 225 235 L 205 235" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowrag)"/>
                
                <!-- 性能指标 -->
                <rect x="50" y="320" width="690" height="60" fill="#fafafa" stroke="#bdbdbd" stroke-width="2" rx="5"/>
                <text x="395" y="345" text-anchor="middle" font-size="15" fill="#2c3e50" font-weight="bold">性能指标</text>
                <text x="70" y="370" font-size="11" fill="#666">• 检索延迟：<50ms</text>
                <text x="230" y="370" font-size="11" fill="#666">• 召回率：95%+</text>
                <text x="380" y="370" font-size="11" fill="#666">• 准确率：90%+</text>
                <text x="530" y="370" font-size="11" fill="#666">• 百万级文档支持</text>
                
                <!-- 说明文字 -->
                <text x="395" y="420" text-anchor="middle" font-size="12" fill="#f44336" font-weight="bold">💡 核心优势：基于真实文档回答，可追溯验证，减少幻觉</text>
                <text x="395" y="445" text-anchor="middle" font-size="11" fill="#666">混合检索 + 重排序 = 召回全面 + 精准相关</text>
            `;
            
            document.getElementById('ragExplanation').innerHTML = `
                <strong>🔄 RAG完整流程</strong><br>
                从文档解析到答案生成的8步流程，实现基于真实文档的智能问答<br>
                <em style="color: #ff9800;">混合检索+重排序确保召回率95%+，准确率90%+</em>
            `;
        }
        
        function showVectorSearch() {
            const svg = document.querySelector('#ragCanvas svg');
            svg.innerHTML = `
                <text x="450" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">向量检索原理</text>
                
                <!-- 文本向量化 -->
                <rect x="50" y="60" width="250" height="180" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="175" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">文本→向量</text>
                
                <text x="60" y="115" font-size="11" fill="#2c3e50" font-weight="bold">输入文本：</text>
                <text x="70" y="135" font-size="10" fill="#666">"如何部署Flink"</text>
                
                <text x="60" y="165" font-size="11" fill="#2c3e50" font-weight="bold">1. 分词：</text>
                <text x="70" y="185" font-size="10" fill="#666">["如何", "部署", "Flink"]</text>
                
                <text x="60" y="215" font-size="11" fill="#2c3e50" font-weight="bold">2. Transformer编码：</text>
                <text x="70" y="235" font-size="10" fill="#666">[0.23, -0.15, 0.88, ..., 0.42]</text>
                
                <!-- 余弦相似度 -->
                <rect x="330" y="60" width="260" height="180" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="460" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">相似度计算</text>
                
                <text x="340" y="115" font-size="11" fill="#2c3e50" font-weight="bold">余弦相似度公式：</text>
                <text x="350" y="135" font-size="10" fill="#666">cos(θ) = (A·B) / (|A|×|B|)</text>
                
                <text x="340" y="165" font-size="11" fill="#2c3e50" font-weight="bold">相似度范围：</text>
                <text x="350" y="185" font-size="10" fill="#4caf50">1.0 → 完全相同</text>
                <text x="350" y="203" font-size="10" fill="#ff9800">0.8 → 很相似</text>
                <text x="350" y="221" font-size="10" fill="#9e9e9e">0.0 → 完全无关</text>
                
                <!-- HNSW索引 -->
                <rect x="620" y="60" width="230" height="180" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="735" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">HNSW索引</text>
                
                <text x="630" y="115" font-size="11" fill="#2c3e50" font-weight="bold">分层导航图：</text>
                <rect x="640" y="125" width="180" height="25" fill="#ffccbc" stroke="#ff5722" stroke-width="1"/>
                <text x="730" y="142" text-anchor="middle" font-size="9" fill="#2c3e50">顶层：快速定位（粗）</text>
                
                <rect x="650" y="155" width="160" height="25" fill="#ffccbc" stroke="#ff5722" stroke-width="1"/>
                <text x="730" y="172" text-anchor="middle" font-size="9" fill="#2c3e50">中层：缩小范围</text>
                
                <rect x="660" y="185" width="140" height="25" fill="#ffccbc" stroke="#ff5722" stroke-width="1"/>
                <text x="730" y="202" text-anchor="middle" font-size="9" fill="#2c3e50">底层：精确查找（细）</text>
                
                <text x="630" y="230" font-size="10" fill="#666">毫秒级检索百万向量</text>
                
                <!-- 性能对比 -->
                <rect x="50" y="270" width="800" height="110" fill="#fafafa" stroke="#bdbdbd" stroke-width="2" rx="5"/>
                <text x="450" y="295" text-anchor="middle" font-size="15" fill="#2c3e50" font-weight="bold">检索性能对比</text>
                
                <rect x="70" y="310" width="250" height="55" fill="#ffebee" stroke="#f44336" stroke-width="2" rx="5"/>
                <text x="195" y="330" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">❌ 暴力搜索</text>
                <text x="80" y="350" font-size="10" fill="#666">• 逐个计算相似度</text>
                <text x="80" y="365" font-size="10" fill="#666">• 100万向量 = 几秒</text>
                
                <rect x="350" y="310" width="250" height="55" fill="#e8f5e9" stroke="#4caf50" stroke-width="2" rx="5"/>
                <text x="475" y="330" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">✓ HNSW索引</text>
                <text x="360" y="350" font-size="10" fill="#666">• 分层导航快速定位</text>
                <text x="360" y="365" font-size="10" fill="#666">• 100万向量 = 毫秒级</text>
                
                <rect x="630" y="310" width="200" height="55" fill="#e1f5fe" stroke="#03a9f4" stroke-width="2" rx="5"/>
                <text x="730" y="330" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">准确率</text>
                <text x="640" y="350" font-size="10" fill="#666">• 准确率：99%+</text>
                <text x="640" y="365" font-size="10" fill="#666">• 几乎无损失</text>
                
                <!-- 训练原理 -->
                <rect x="50" y="400" width="800" height="80" fill="#f3e5f5" stroke="#9c27b0" stroke-width="2" rx="5"/>
                <text x="450" y="425" text-anchor="middle" font-size="15" fill="#2c3e50" font-weight="bold">为什么相似文本向量会很近？</text>
                <text x="60" y="450" font-size="11" fill="#666">• 对比学习训练：相似句子向量靠近（正样本），不相似句子向量远离（负样本）</text>
                <text x="60" y="470" font-size="11" fill="#666">• 例如："如何部署Flink" 与 "Flink怎么安装" 相似度0.92（很近），与 "今天天气真好" 相似度0.15（很远）</text>
            `;
            
            document.getElementById('ragExplanation').innerHTML = `
                <strong>🔍 向量检索原理</strong><br>
                通过Transformer编码文本语义，用余弦相似度衡量相关性<br>
                <em style="color: #ff9800;">HNSW索引：百万级向量毫秒级检索，准确率99%+</em>
            `;
        }
        
        function resetRAGView() {
            const svg = document.querySelector('#ragCanvas svg');
            svg.innerHTML = `
                <text x="450" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">RAG工作原理</text>
                <text x="450" y="250" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细流程</text>
            `;
            
            document.getElementById('ragExplanation').innerHTML = '点击按钮查看RAG流程或向量检索原理';
        }
        
        // ==================== Hudi可视化 ====================
        function showHudiArchitecture() {
            const svg = document.querySelector('#hudiCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Hudi数据湖架构</text>
                
                <!-- Flink写入 -->
                <rect x="50" y="60" width="140" height="80" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="120" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">Flink</text>
                <text x="60" y="110" font-size="11" fill="#666">• 实时计算</text>
                <text x="60" y="128" font-size="11" fill="#666">• 数据清洗</text>
                
                <!-- Kafka数据源 -->
                <rect x="50" y="160" width="140" height="60" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="120" y="185" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">Kafka</text>
                <text x="60" y="208" font-size="11" fill="#666">原始数据流</text>
                
                <!-- Hudi核心 -->
                <rect x="230" y="60" width="340" height="280" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="400" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">Apache Hudi</text>
                
                <!-- Timeline -->
                <rect x="250" y="100" width="140" height="70" fill="#c8e6c9" stroke="#66bb6a" stroke-width="2" rx="5"/>
                <text x="320" y="120" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">Timeline</text>
                <text x="260" y="140" font-size="10" fill="#666">• 记录所有操作</text>
                <text x="260" y="158" font-size="10" fill="#666">• MVCC版本控制</text>
                
                <!-- Index -->
                <rect x="410" y="100" width="140" height="70" fill="#c8e6c9" stroke="#66bb6a" stroke-width="2" rx="5"/>
                <text x="480" y="120" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">Index</text>
                <text x="420" y="140" font-size="10" fill="#666">• 快速定位记录</text>
                <text x="420" y="158" font-size="10" fill="#666">• BloomFilter索引</text>
                
                <!-- 表类型 -->
                <rect x="250" y="190" width="140" height="70" fill="#bbdefb" stroke="#2196f3" stroke-width="2" rx="5"/>
                <text x="320" y="210" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">COW表</text>
                <text x="260" y="230" font-size="10" fill="#666">• 查询快</text>
                <text x="260" y="248" font-size="10" fill="#666">• 适合实时增量</text>
                
                <rect x="410" y="190" width="140" height="70" fill="#fff9c4" stroke="#fbc02d" stroke-width="2" rx="5"/>
                <text x="480" y="210" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">MOR表</text>
                <text x="420" y="230" font-size="10" fill="#666">• 写入快</text>
                <text x="420" y="248" font-size="10" fill="#666">• 适合CDC同步</text>
                
                <!-- 元数据同步 -->
                <rect x="250" y="280" width="300" height="50" fill="#f3e5f5" stroke="#9c27b0" stroke-width="2" rx="5"/>
                <text x="400" y="302" text-anchor="middle" font-size="12" fill="#2c3e50" font-weight="bold">元数据同步</text>
                <text x="260" y="320" font-size="10" fill="#666">自动同步到Hive Metastore</text>
                
                <!-- HDFS存储 -->
                <rect x="610" y="60" width="140" height="120" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="8"/>
                <text x="680" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">HDFS</text>
                <text x="620" y="110" font-size="11" fill="#666">• Parquet文件</text>
                <text x="620" y="128" font-size="11" fill="#666">• 分区存储</text>
                <text x="620" y="146" font-size="11" fill="#666">• 副本保证</text>
                <text x="620" y="164" font-size="11" fill="#666">• 高可用</text>
                
                <!-- 查询引擎 -->
                <rect x="610" y="200" width="140" height="140" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="680" y="220" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">查询引擎</text>
                <text x="620" y="245" font-size="11" fill="#666">• Hive</text>
                <text x="620" y="263" font-size="11" fill="#666">• Spark</text>
                <text x="620" y="281" font-size="11" fill="#666">• Presto</text>
                <text x="620" y="299" font-size="11" fill="#666">• Impala</text>
                <text x="680" y="325" text-anchor="middle" font-size="10" fill="#ff9800" font-weight="bold">流批一体查询</text>
                
                <!-- 箭头 -->
                <defs>
                    <marker id="arrowhudi" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto">
                        <polygon points="0 0, 8 3, 0 6" fill="#4caf50" />
                    </marker>
                </defs>
                
                <path d="M 190 100 L 225 120" stroke="#2196f3" stroke-width="2" fill="none" marker-end="url(#arrowhudi)"/>
                <path d="M 120 220 L 120 240 L 230 240" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowhudi)" stroke-dasharray="5,5"/>
                <path d="M 570 150 L 605 130" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowhudi)"/>
                <path d="M 570 240 L 605 260" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowhudi)"/>
                
                <text x="200" y="115" font-size="10" fill="#2196f3" font-weight="bold">写入</text>
                <text x="585" y="145" font-size="10" fill="#4caf50" font-weight="bold">存储</text>
                <text x="585" y="270" font-size="10" fill="#ff9800" font-weight="bold">查询</text>
                
                <!-- 性能指标 -->
                <text x="400" y="375" text-anchor="middle" font-size="12" fill="#f44336" font-weight="bold">💡 核心优势：流批一体、分钟级更新、ACID事务、时间旅行查询</text>
            `;
            
            document.getElementById('hudiExplanation').innerHTML = `
                <strong>🏗️ Hudi数据湖架构</strong><br>
                Flink实时写入→Hudi管理→HDFS存储→多引擎查询<br>
                <em style="color: #4caf50;">支持分钟级增量更新，实现流批一体</em>
            `;
        }
        
        function showCOWvsMOR() {
            const svg = document.querySelector('#hudiCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">COW vs MOR 表类型对比</text>
                
                <!-- COW -->
                <rect x="50" y="60" width="340" height="300" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="220" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">Copy-On-Write（COW）</text>
                
                <text x="70" y="120" font-size="13" fill="#2c3e50" font-weight="bold">工作原理：</text>
                <text x="80" y="145" font-size="11" fill="#666">• 更新时重写整个文件</text>
                <text x="80" y="163" font-size="11" fill="#666">• 只有Parquet列式文件</text>
                <text x="80" y="181" font-size="11" fill="#666">• 写入慢、查询快</text>
                
                <text x="70" y="210" font-size="13" fill="#2c3e50" font-weight="bold">优点：</text>
                <text x="80" y="235" font-size="11" fill="#4caf50">✓ 查询性能高（直接读）</text>
                <text x="80" y="253" font-size="11" fill="#4caf50">✓ 无需合并，简单直接</text>
                <text x="80" y="271" font-size="11" fill="#4caf50">✓ 适合查询频繁场景</text>
                
                <text x="70" y="300" font-size="13" fill="#2c3e50" font-weight="bold">缺点：</text>
                <text x="80" y="325" font-size="11" fill="#f44336">✗ 写入慢（需重写文件）</text>
                <text x="80" y="343" font-size="11" fill="#f44336">✗ 写放大严重</text>
                
                <!-- MOR -->
                <rect x="410" y="60" width="340" height="300" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="580" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">Merge-On-Read（MOR）</text>
                
                <text x="430" y="120" font-size="13" fill="#2c3e50" font-weight="bold">工作原理：</text>
                <text x="440" y="145" font-size="11" fill="#666">• 更新追加到Delta Log</text>
                <text x="440" y="163" font-size="11" fill="#666">• Parquet + Avro日志文件</text>
                <text x="440" y="181" font-size="11" fill="#666">• 写入快、查询时合并</text>
                
                <text x="430" y="210" font-size="13" fill="#2c3e50" font-weight="bold">优点：</text>
                <text x="440" y="235" font-size="11" fill="#4caf50">✓ 写入性能高（追加）</text>
                <text x="440" y="253" font-size="11" fill="#4caf50">✓ 减少写放大</text>
                <text x="440" y="271" font-size="11" fill="#4caf50">✓ 适合CDC频繁更新</text>
                
                <text x="430" y="300" font-size="13" fill="#2c3e50" font-weight="bold">缺点：</text>
                <text x="440" y="325" font-size="11" fill="#f44336">✗ 查询慢（需合并）</text>
                <text x="440" y="343" font-size="11" fill="#f44336">✗ 需要Compaction</text>
                
                <!-- 对比说明 -->
                <rect x="50" y="370" width="700" height="20" fill="#fafafa" stroke="#bdbdbd" stroke-width="1" rx="3"/>
                <text x="400" y="385" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">选择建议：查询多用COW，更新多用MOR，CDC场景首选MOR</text>
            `;
            
            document.getElementById('hudiExplanation').innerHTML = `
                <strong>📊 COW vs MOR</strong><br>
                <span style="color: #2196f3;">COW：</span>适合实时增量数据，查询频繁场景<br>
                <span style="color: #ff9800;">MOR：</span>适合CDC数据同步，更新频繁场景
            `;
        }
        
        function resetHudiView() {
            const svg = document.querySelector('#hudiCanvas svg');
            svg.innerHTML = `
                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">Hudi工作原理</text>
                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
            `;
            
            document.getElementById('hudiExplanation').innerHTML = '点击按钮查看Hudi架构或表类型对比';
        }
        
        // ==================== K8s可视化 ====================
        function showK8sArchitecture() {
            const svg = document.querySelector('#k8sCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">K8s架构</text>
                
                <!-- Master节点 -->
                <rect x="50" y="60" width="320" height="280" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="210" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">Master节点（控制平面）</text>
                
                <!-- API Server -->
                <rect x="70" y="100" width="130" height="60" fill="#bbdefb" stroke="#1976d2" stroke-width="2" rx="5"/>
                <text x="135" y="120" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">API Server</text>
                <text x="80" y="140" font-size="10" fill="#666">• 集群入口</text>
                <text x="80" y="155" font-size="10" fill="#666">• RESTful API</text>
                
                <!-- Scheduler -->
                <rect x="220" y="100" width="130" height="60" fill="#bbdefb" stroke="#1976d2" stroke-width="2" rx="5"/>
                <text x="285" y="120" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">Scheduler</text>
                <text x="230" y="140" font-size="10" fill="#666">• 调度Pod</text>
                <text x="230" y="155" font-size="10" fill="#666">• 资源分配</text>
                
                <!-- Controller Manager -->
                <rect x="70" y="170" width="130" height="60" fill="#bbdefb" stroke="#1976d2" stroke-width="2" rx="5"/>
                <text x="135" y="190" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">Controller Mgr</text>
                <text x="80" y="210" font-size="10" fill="#666">• 监控状态</text>
                <text x="80" y="225" font-size="10" fill="#666">• 自愈调节</text>
                
                <!-- etcd -->
                <rect x="220" y="170" width="130" height="60" fill="#bbdefb" stroke="#1976d2" stroke-width="2" rx="5"/>
                <text x="285" y="190" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">etcd</text>
                <text x="230" y="210" font-size="10" fill="#666">• 集群数据库</text>
                <text x="230" y="225" font-size="10" fill="#666">• 配置存储</text>
                
                <!-- 说明 -->
                <text x="210" y="260" text-anchor="middle" font-size="11" fill="#2196f3" font-weight="bold">负责集群管理和决策</text>
                <text x="210" y="275" text-anchor="middle" font-size="10" fill="#666">通常3个节点做HA</text>
                <text x="210" y="290" text-anchor="middle" font-size="10" fill="#666">不运行应用容器</text>
                
                <!-- Worker节点 -->
                <rect x="400" y="60" width="350" height="280" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="575" y="85" text-anchor="middle" font-size="16" fill="#2c3e50" font-weight="bold">Worker节点（数据平面）</text>
                
                <!-- Kubelet -->
                <rect x="420" y="100" width="150" height="60" fill="#c8e6c9" stroke="#388e3c" stroke-width="2" rx="5"/>
                <text x="495" y="120" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">Kubelet</text>
                <text x="430" y="140" font-size="10" fill="#666">• 管理Pod生命周期</text>
                <text x="430" y="155" font-size="10" fill="#666">• 上报节点状态</text>
                
                <!-- Kube-proxy -->
                <rect x="590" y="100" width="140" height="60" fill="#c8e6c9" stroke="#388e3c" stroke-width="2" rx="5"/>
                <text x="660" y="120" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">Kube-proxy</text>
                <text x="600" y="140" font-size="10" fill="#666">• 网络代理</text>
                <text x="600" y="155" font-size="10" fill="#666">• 负载均衡</text>
                
                <!-- Pod示例 -->
                <rect x="420" y="180" width="310" height="150" fill="#f1f8e9" stroke="#689f38" stroke-width="2" rx="5"/>
                <text x="575" y="200" text-anchor="middle" font-size="13" fill="#2c3e50" font-weight="bold">Pod（最小部署单元）</text>
                
                <rect x="435" y="215" width="90" height="55" fill="#81c784" stroke="#388e3c" stroke-width="1" rx="3"/>
                <text x="480" y="235" text-anchor="middle" font-size="11" fill="white" font-weight="bold">Container 1</text>
                <text x="480" y="253" text-anchor="middle" font-size="9" fill="white">应用容器</text>
                <text x="480" y="267" text-anchor="middle" font-size="9" fill="white">Flink/Nginx</text>
                
                <rect x="540" y="215" width="90" height="55" fill="#81c784" stroke="#388e3c" stroke-width="1" rx="3"/>
                <text x="585" y="235" text-anchor="middle" font-size="11" fill="white" font-weight="bold">Container 2</text>
                <text x="585" y="253" text-anchor="middle" font-size="9" fill="white">Sidecar</text>
                <text x="585" y="267" text-anchor="middle" font-size="9" fill="white">日志收集</text>
                
                <rect x="645" y="215" width="70" height="55" fill="#a5d6a7" stroke="#388e3c" stroke-width="1" rx="3"/>
                <text x="680" y="235" text-anchor="middle" font-size="11" fill="#2c3e50" font-weight="bold">Volume</text>
                <text x="680" y="253" text-anchor="middle" font-size="9" fill="#2c3e50">持久化</text>
                <text x="680" y="267" text-anchor="middle" font-size="9" fill="#2c3e50">存储</text>
                
                <text x="575" y="295" text-anchor="middle" font-size="10" fill="#666">共享网络命名空间、localhost通信</text>
                <text x="575" y="310" text-anchor="middle" font-size="10" fill="#666">共享存储卷、可挂载PVC</text>
                
                <!-- 箭头 -->
                <defs>
                    <marker id="arrowk8s" markerWidth="8" markerHeight="8" refX="7" refY="3" orient="auto">
                        <polygon points="0 0, 8 3, 0 6" fill="#ff9800" />
                    </marker>
                </defs>
                
                <path d="M 370 130 L 395 130" stroke="#ff9800" stroke-width="2" fill="none" marker-end="url(#arrowk8s)"/>
                <text x="380" y="120" font-size="10" fill="#ff9800" font-weight="bold">调度</text>
                
                <path d="M 570 200 L 570 165" stroke="#4caf50" stroke-width="2" fill="none" marker-end="url(#arrowk8s)" stroke-dasharray="5,5"/>
                <text x="575" y="185" font-size="10" fill="#4caf50" font-weight="bold">上报</text>
                
                <!-- 说明 -->
                <text x="400" y="370" text-anchor="middle" font-size="12" fill="#f44336" font-weight="bold">💡 Master负责决策，Worker负责运行，etcd存储配置</text>
            `;
            
            document.getElementById('k8sExplanation').innerHTML = `
                <strong>🏗️ K8s架构</strong><br>
                <span style="color: #2196f3;">Master：</span>集群大脑，负责调度和管理<br>
                <span style="color: #4caf50;">Worker：</span>工作节点，运行实际的应用容器
            `;
        }
        
        function showK8sComponents() {
            const svg = document.querySelector('#k8sCanvas svg');
            svg.innerHTML = `
                <text x="400" y="25" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">K8s核心组件</text>
                
                <!-- Pod -->
                <rect x="50" y="60" width="170" height="120" fill="#e3f2fd" stroke="#2196f3" stroke-width="3" rx="8"/>
                <text x="135" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">Pod</text>
                <text x="60" y="110" font-size="11" fill="#666">• 最小部署单元</text>
                <text x="60" y="128" font-size="11" fill="#666">• 1个或多个容器</text>
                <text x="60" y="146" font-size="11" fill="#666">• 共享网络/存储</text>
                <text x="135" y="168" text-anchor="middle" font-size="10" fill="#2196f3" font-weight="bold">基础单元</text>
                
                <!-- Service -->
                <rect x="240" y="60" width="170" height="120" fill="#e8f5e9" stroke="#4caf50" stroke-width="3" rx="8"/>
                <text x="325" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">Service</text>
                <text x="250" y="110" font-size="11" fill="#666">• Pod访问入口</text>
                <text x="250" y="128" font-size="11" fill="#666">• 负载均衡</text>
                <text x="250" y="146" font-size="11" fill="#666">• 固定IP和DNS</text>
                <text x="325" y="168" text-anchor="middle" font-size="10" fill="#4caf50" font-weight="bold">服务发现</text>
                
                <!-- Deployment -->
                <rect x="430" y="60" width="170" height="120" fill="#fff3e0" stroke="#ff9800" stroke-width="3" rx="8"/>
                <text x="515" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">Deployment</text>
                <text x="440" y="110" font-size="11" fill="#666">• 管理Pod副本</text>
                <text x="440" y="128" font-size="11" fill="#666">• 滚动更新</text>
                <text x="440" y="146" font-size="11" fill="#666">• 版本回滚</text>
                <text x="515" y="168" text-anchor="middle" font-size="10" fill="#ff9800" font-weight="bold">应用部署</text>
                
                <!-- ConfigMap -->
                <rect x="620" y="60" width="130" height="120" fill="#f3e5f5" stroke="#9c27b0" stroke-width="3" rx="8"/>
                <text x="685" y="85" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">ConfigMap</text>
                <text x="630" y="110" font-size="11" fill="#666">• 配置管理</text>
                <text x="630" y="128" font-size="11" fill="#666">• 环境变量</text>
                <text x="630" y="146" font-size="11" fill="#666">• 配置文件</text>
                <text x="685" y="168" text-anchor="middle" font-size="10" fill="#9c27b0" font-weight="bold">配置中心</text>
                
                <!-- PV/PVC -->
                <rect x="50" y="200" width="170" height="120" fill="#ffebee" stroke="#f44336" stroke-width="3" rx="8"/>
                <text x="135" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">PV / PVC</text>
                <text x="60" y="250" font-size="11" fill="#666">• 持久化存储</text>
                <text x="60" y="268" font-size="11" fill="#666">• PV：存储资源</text>
                <text x="60" y="286" font-size="11" fill="#666">• PVC：存储请求</text>
                <text x="135" y="308" text-anchor="middle" font-size="10" fill="#f44336" font-weight="bold">数据持久化</text>
                
                <!-- Namespace -->
                <rect x="240" y="200" width="170" height="120" fill="#e1f5fe" stroke="#03a9f4" stroke-width="3" rx="8"/>
                <text x="325" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">Namespace</text>
                <text x="250" y="250" font-size="11" fill="#666">• 资源隔离</text>
                <text x="250" y="268" font-size="11" fill="#666">• 多租户</text>
                <text x="250" y="286" font-size="11" fill="#666">• 环境划分</text>
                <text x="325" y="308" text-anchor="middle" font-size="10" fill="#03a9f4" font-weight="bold">逻辑隔离</text>
                
                <!-- Ingress -->
                <rect x="430" y="200" width="170" height="120" fill="#fff9c4" stroke="#fbc02d" stroke-width="3" rx="8"/>
                <text x="515" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">Ingress</text>
                <text x="440" y="250" font-size="11" fill="#666">• 外部访问入口</text>
                <text x="440" y="268" font-size="11" fill="#666">• 路由规则</text>
                <text x="440" y="286" font-size="11" fill="#666">• 负载均衡</text>
                <text x="515" y="308" text-anchor="middle" font-size="10" fill="#fbc02d" font-weight="bold">流量入口</text>
                
                <!-- StatefulSet -->
                <rect x="620" y="200" width="130" height="120" fill="#e0f2f1" stroke="#009688" stroke-width="3" rx="8"/>
                <text x="685" y="225" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">StatefulSet</text>
                <text x="630" y="250" font-size="11" fill="#666">• 有状态应用</text>
                <text x="630" y="268" font-size="11" fill="#666">• 固定标识</text>
                <text x="630" y="286" font-size="11" fill="#666">• 顺序部署</text>
                <text x="685" y="308" text-anchor="middle" font-size="10" fill="#009688" font-weight="bold">数据库/MQ</text>
                
                <!-- 说明 -->
                <rect x="50" y="340" width="700" height="50" fill="#fafafa" stroke="#bdbdbd" stroke-width="2" rx="5"/>
                <text x="400" y="362" text-anchor="middle" font-size="14" fill="#2c3e50" font-weight="bold">K8s核心能力</text>
                <text x="60" y="380" font-size="11" fill="#666">自动调度 • 弹性扩缩容 • 自愈恢复 • 服务发现 • 负载均衡 • 滚动更新 • 配置管理 • 存储编排</text>
            `;
            
            document.getElementById('k8sExplanation').innerHTML = `
                <strong>📦 K8s核心组件</strong><br>
                Pod是基础单元，Service提供服务发现，Deployment管理部署<br>
                <em style="color: #2196f3;">8大核心组件，实现云原生应用的完整生命周期管理</em>
            `;
        }
        
        function resetK8sView() {
            const svg = document.querySelector('#k8sCanvas svg');
            svg.innerHTML = `
                <text x="400" y="30" text-anchor="middle" font-size="18" fill="#2c3e50" font-weight="bold">K8s工作原理</text>
                <text x="400" y="200" text-anchor="middle" font-size="14" fill="#666">点击上方按钮查看详细架构</text>
            `;
            
            document.getElementById('k8sExplanation').innerHTML = '点击按钮查看K8s架构或核心组件';
        }
    </script>
</body>
</html>
